line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package UR::DataSource::QueryPlan; |
2
|
143
|
|
|
143
|
|
4819
|
use strict; |
|
143
|
|
|
|
|
196
|
|
|
143
|
|
|
|
|
4277
|
|
3
|
143
|
|
|
143
|
|
529
|
use warnings; |
|
143
|
|
|
|
|
172
|
|
|
143
|
|
|
|
|
3706
|
|
4
|
143
|
|
|
143
|
|
482
|
use UR; |
|
143
|
|
|
|
|
164
|
|
|
143
|
|
|
|
|
777
|
|
5
|
|
|
|
|
|
|
our $VERSION = "0.46"; # UR $VERSION; |
6
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
# this class is an evolving attempt to formalize |
8
|
|
|
|
|
|
|
# the blob of cached value used for query construction |
9
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
class UR::DataSource::QueryPlan { |
11
|
|
|
|
|
|
|
is => 'UR::Value', |
12
|
|
|
|
|
|
|
id_by => [ |
13
|
|
|
|
|
|
|
rule_template => { is => 'UR::BoolExpr::Template', id_by => ['subject_class_name','logic_type','logic_detail','constant_values_id'] }, |
14
|
|
|
|
|
|
|
data_source => { is => 'UR::DataSource', id_by => 'data_source_id' }, |
15
|
|
|
|
|
|
|
], |
16
|
|
|
|
|
|
|
has => [ |
17
|
|
|
|
|
|
|
limit => { is => 'Integer', via => 'rule_template', to => 'limit' }, |
18
|
|
|
|
|
|
|
offset => { is => 'Integer', via => 'rule_template', to => 'offset' }, |
19
|
|
|
|
|
|
|
], |
20
|
|
|
|
|
|
|
has_transient => [ |
21
|
|
|
|
|
|
|
_is_initialized => { is => 'Boolean' }, |
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
needs_further_boolexpr_evaluation_after_loading => { is => 'Boolean' }, |
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
# data tracked for the whole query by property,alias,join_id |
26
|
|
|
|
|
|
|
_delegation_chain_data => { is => 'HASH' }, |
27
|
|
|
|
|
|
|
_alias_data => { is => 'HASH' }, |
28
|
|
|
|
|
|
|
_join_data => { is => 'HASH' }, |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
# the old $alias_num |
31
|
|
|
|
|
|
|
_alias_count => { is => 'Number' }, |
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
# the old @sql_joins |
34
|
|
|
|
|
|
|
_db_joins => { is => 'ARRAY' }, |
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
# the new @obj_joins |
37
|
|
|
|
|
|
|
_obj_joins => { is => 'ARRAY' }, |
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
# the old all_table_properties, which has a small array of loading info |
40
|
|
|
|
|
|
|
_db_column_data => { is => 'ARRAY' }, |
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
# the old hashes by the same names |
43
|
|
|
|
|
|
|
_group_by_property_names => { is => 'HASH' }, |
44
|
|
|
|
|
|
|
_order_by_property_names => { is => 'HASH' }, |
45
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
_sql_filters => { is => 'ARRAY' }, |
47
|
|
|
|
|
|
|
_sql_params => { is => 'ARRAY' }, |
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
lob_column_names => {}, |
50
|
|
|
|
|
|
|
lob_column_positions => {}, |
51
|
|
|
|
|
|
|
query_config => {}, |
52
|
|
|
|
|
|
|
post_process_results_callback => {}, |
53
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
select_clause => {}, |
55
|
|
|
|
|
|
|
select_hint => {}, |
56
|
|
|
|
|
|
|
from_clause => {}, |
57
|
|
|
|
|
|
|
where_clause => {}, |
58
|
|
|
|
|
|
|
connect_by_clause => {}, |
59
|
|
|
|
|
|
|
group_by_clause => {}, |
60
|
|
|
|
|
|
|
order_by_columns => {}, |
61
|
|
|
|
|
|
|
order_by_non_column_data => {}, # flag that's true if asked to order_by something not in the data source |
62
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
sql_params => {}, |
64
|
|
|
|
|
|
|
filter_specs => {}, |
65
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
property_names_in_resultset_order => {}, |
67
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
rule_template_id => {}, |
69
|
|
|
|
|
|
|
rule_template_id_without_recursion_desc => {}, |
70
|
|
|
|
|
|
|
rule_template_without_recursion_desc => {}, |
71
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
joins => {}, |
73
|
|
|
|
|
|
|
recursion_desc => {}, |
74
|
|
|
|
|
|
|
recurse_property_on_this_row => {}, |
75
|
|
|
|
|
|
|
recurse_property_referencing_other_rows => {}, |
76
|
|
|
|
|
|
|
recurse_resolution_by_iteration => {}, # For data sources that don't support recursive queries |
77
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
joins_across_data_sources => {}, # context _resolve_query_plan_for_ds_and_bxt |
79
|
|
|
|
|
|
|
loading_templates => {}, |
80
|
|
|
|
|
|
|
class_name => {}, |
81
|
|
|
|
|
|
|
rule_matches_all => {}, |
82
|
|
|
|
|
|
|
rule_template_is_id_only => {}, |
83
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
sub_typing_property => {}, |
85
|
|
|
|
|
|
|
class_table_name => {}, |
86
|
|
|
|
|
|
|
rule_template_specifies_value_for_subtype => {}, |
87
|
|
|
|
|
|
|
sub_classification_meta_class_name => {}, |
88
|
|
|
|
|
|
|
] |
89
|
|
|
|
|
|
|
}; |
90
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
sub _load { |
93
|
3838
|
|
|
3838
|
|
5220
|
my $class = shift; |
94
|
3838
|
|
|
|
|
4269
|
my $rule = shift; |
95
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
# See if the requested object is loaded. |
97
|
3838
|
|
|
|
|
12469
|
my @loaded = $UR::Context::current->get_objects_for_class_and_rule($class,$rule,0); |
98
|
3838
|
100
|
|
|
|
15291
|
return $class->context_return(@loaded) if @loaded; |
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
# Auto generate the object on the fly. |
101
|
798
|
|
|
|
|
2185
|
my $id = $rule->value_for_id; |
102
|
798
|
50
|
|
|
|
2116
|
unless (defined $id) { |
103
|
|
|
|
|
|
|
#$DB::single = 1; |
104
|
0
|
|
|
|
|
0
|
Carp::croak "No id specified for loading members of an infinite set ($class)!" |
105
|
|
|
|
|
|
|
} |
106
|
798
|
|
|
|
|
2313
|
my $class_meta = $class->__meta__; |
107
|
798
|
|
|
|
|
1883
|
my @p = (id => $id); |
108
|
798
|
50
|
|
|
|
2571
|
if (my $alt_ids = $class_meta->{id_by}) { |
109
|
798
|
50
|
|
|
|
2243
|
if (@$alt_ids == 1) { |
110
|
0
|
|
|
|
|
0
|
push @p, $alt_ids->[0] => $id; |
111
|
|
|
|
|
|
|
} |
112
|
|
|
|
|
|
|
else { |
113
|
798
|
|
|
|
|
2992
|
my ($rule, %extra) = UR::BoolExpr->resolve_normalized($class, $rule); |
114
|
798
|
|
|
|
|
2358
|
push @p, $rule->params_list; |
115
|
|
|
|
|
|
|
} |
116
|
|
|
|
|
|
|
} |
117
|
|
|
|
|
|
|
|
118
|
798
|
|
|
|
|
4194
|
my $obj = $UR::Context::current->_construct_object($class, @p); |
119
|
|
|
|
|
|
|
|
120
|
798
|
50
|
|
|
|
4338
|
if (my $method_name = $class_meta->sub_classification_method_name) { |
121
|
0
|
|
|
|
|
0
|
my($rule, %extra) = UR::BoolExpr->resolve_normalized($class, $rule); |
122
|
0
|
|
|
|
|
0
|
my $sub_class_name = $obj->$method_name; |
123
|
0
|
0
|
|
|
|
0
|
if ($sub_class_name ne $class) { |
124
|
|
|
|
|
|
|
# delegate to the sub-class to create the object |
125
|
0
|
|
|
|
|
0
|
$UR::Context::current->_abandon_object($obj); |
126
|
0
|
|
|
|
|
0
|
$obj = $UR::Context::current->_construct_object($sub_class_name,$rule); |
127
|
0
|
|
|
|
|
0
|
$obj->__signal_change__("load"); |
128
|
0
|
|
|
|
|
0
|
return $obj; |
129
|
|
|
|
|
|
|
} |
130
|
|
|
|
|
|
|
# fall through if the class names match |
131
|
|
|
|
|
|
|
} |
132
|
|
|
|
|
|
|
|
133
|
798
|
|
|
|
|
3226
|
$obj->__signal_change__("load"); |
134
|
798
|
|
|
|
|
2805
|
return $obj; |
135
|
|
|
|
|
|
|
} |
136
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
# these hash keys are probably removable |
138
|
|
|
|
|
|
|
# because they are not above, they will be deleted if _init sets them |
139
|
|
|
|
|
|
|
# this exists primarily as a cleanup target list |
140
|
|
|
|
|
|
|
my @extra = qw( |
141
|
|
|
|
|
|
|
id_properties |
142
|
|
|
|
|
|
|
direct_table_properties |
143
|
|
|
|
|
|
|
all_table_properties |
144
|
|
|
|
|
|
|
sub_classification_method_name |
145
|
|
|
|
|
|
|
subclassify_by |
146
|
|
|
|
|
|
|
properties_meta_in_resultset_order |
147
|
|
|
|
|
|
|
all_properties |
148
|
|
|
|
|
|
|
rule_specifies_id |
149
|
|
|
|
|
|
|
all_id_property_names |
150
|
|
|
|
|
|
|
id_property_sorter |
151
|
|
|
|
|
|
|
properties_for_params |
152
|
|
|
|
|
|
|
first_table_name |
153
|
|
|
|
|
|
|
base_joins |
154
|
|
|
|
|
|
|
parent_class_objects |
155
|
|
|
|
|
|
|
); |
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
sub _init { |
158
|
798
|
|
|
798
|
|
1336
|
my $self = shift; |
159
|
|
|
|
|
|
|
|
160
|
798
|
50
|
|
|
|
2011
|
Carp::confess("already initialized???") if $self->_is_initialized; |
161
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
# We could have this sub-classify by data source type, but right |
163
|
|
|
|
|
|
|
# now it's conditional logic because we'll likely remove the distinctions. |
164
|
|
|
|
|
|
|
# This will work because we'll separate out the ds-specific portion |
165
|
|
|
|
|
|
|
# and call methods on the DS to get that part. |
166
|
798
|
|
|
|
|
2995
|
my $ds = $self->data_source; |
167
|
798
|
100
|
|
|
|
5477
|
if ($ds->isa("UR::DataSource::RDBMS")) { |
|
|
100
|
|
|
|
|
|
168
|
655
|
|
|
|
|
2646
|
$self->_init_light(); |
169
|
655
|
|
|
|
|
2890
|
$self->_init_rdbms(); |
170
|
|
|
|
|
|
|
} |
171
|
|
|
|
|
|
|
elsif ($ds->isa('UR::DataSource::Filesystem')) { |
172
|
33
|
|
|
|
|
121
|
$self->_init_core(); |
173
|
33
|
|
|
|
|
129
|
$self->_init_filesystem(); |
174
|
|
|
|
|
|
|
} |
175
|
|
|
|
|
|
|
else { |
176
|
|
|
|
|
|
|
# Once all callers are using the API for this we won't need "_init". |
177
|
110
|
|
|
|
|
437
|
$self->_init_core(); |
178
|
110
|
100
|
|
|
|
851
|
$self->_init_default() if $ds->isa("UR::DataSource::Default"); |
179
|
|
|
|
|
|
|
#$self->_init_remote_cache() if $ds->isa("UR::DataSource::RemoteCache"); |
180
|
|
|
|
|
|
|
} |
181
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
# This object is currently still used as a hashref, but the properties |
183
|
|
|
|
|
|
|
# are a declaration of the part of the hashref data we are still dependent upon. |
184
|
|
|
|
|
|
|
# This removes the other properties to ensure this is the case. |
185
|
|
|
|
|
|
|
# Next steps are to clean up the code below to not produce the data, |
186
|
|
|
|
|
|
|
# then this loop can throw an exception if extra untracked data is found. |
187
|
798
|
|
|
|
|
5257
|
for my $key (keys %$self) { |
188
|
43368
|
100
|
|
|
|
145583
|
next if $self->can($key); |
189
|
10666
|
|
|
|
|
381428
|
delete $self->{$key}; |
190
|
|
|
|
|
|
|
} |
191
|
|
|
|
|
|
|
|
192
|
798
|
|
|
|
|
6515
|
$self->_is_initialized(1); |
193
|
798
|
|
|
|
|
1690
|
return $self; |
194
|
|
|
|
|
|
|
} |
195
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
sub _determine_complete_order_by_list { |
198
|
688
|
|
|
688
|
|
1224
|
my($self, $rule_template, $class_data, $db_property_data) = @_; |
199
|
|
|
|
|
|
|
|
200
|
688
|
|
|
|
|
1987
|
my $class_meta = $rule_template->subject_class_name->__meta__; |
201
|
688
|
|
50
|
|
|
2336
|
my $order_by_columns = $class_data->{order_by_columns} || []; |
202
|
688
|
|
|
|
|
1803
|
my $order_by = $rule_template->order_by; |
203
|
688
|
|
|
|
|
2277
|
my $ds = $self->data_source; |
204
|
|
|
|
|
|
|
|
205
|
688
|
|
|
|
|
1107
|
my %order_by_property_names; |
206
|
|
|
|
|
|
|
my $order_by_non_column_data; |
207
|
688
|
100
|
|
|
|
1880
|
if ($order_by) { |
208
|
43
|
|
|
|
|
101
|
my %db_property_data_map = map { $_->[1]->property_name => $_ } @$db_property_data; |
|
135
|
|
|
|
|
253
|
|
209
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
# we only pull back columns we're ordering by if there is ordering happening |
211
|
43
|
|
|
|
|
89
|
my %is_descending; |
212
|
|
|
|
|
|
|
my @column_data; |
213
|
43
|
|
|
|
|
95
|
for my $name (@$order_by) { |
214
|
48
|
|
|
|
|
81
|
my $order_by_prop = $name; |
215
|
48
|
100
|
|
|
|
250
|
if ($order_by_prop =~ m/^(-|\+)(.*)$/) { |
216
|
20
|
|
|
|
|
83
|
$order_by_prop = $2; |
217
|
20
|
|
|
|
|
65
|
$is_descending{$order_by_prop} = $1 eq '-'; |
218
|
|
|
|
|
|
|
} |
219
|
|
|
|
|
|
|
|
220
|
48
|
|
|
|
|
216
|
my($order_by_prop_meta) = $class_meta->_concrete_property_meta_for_class_and_name($order_by_prop); |
221
|
48
|
50
|
|
|
|
114
|
unless ($order_by_prop_meta) { |
222
|
0
|
|
|
|
|
0
|
Carp::croak("Cannot order by '$name': Class " |
223
|
|
|
|
|
|
|
. $class_meta->class_name |
224
|
|
|
|
|
|
|
. " has no property named '$order_by_prop'"); |
225
|
|
|
|
|
|
|
} |
226
|
|
|
|
|
|
|
|
227
|
48
|
100
|
|
|
|
201
|
$name = ( $is_descending{$order_by_prop} ? '-' : '' ) . $order_by_prop_meta->property_name; |
228
|
48
|
100
|
|
|
|
117
|
if ($order_by_property_names{$name} = $db_property_data_map{$order_by_prop_meta->property_name}) { # yes, single = |
229
|
45
|
|
|
|
|
77
|
push @column_data, $order_by_property_names{$name}; |
230
|
|
|
|
|
|
|
|
231
|
45
|
|
|
|
|
229
|
my $table_column_names = $ds->_select_clause_columns_for_table_property_data($column_data[-1]); |
232
|
45
|
|
|
|
|
116
|
$is_descending{$table_column_names->[0]} = $is_descending{$order_by_prop}; # copy for table.column designation |
233
|
45
|
|
|
|
|
133
|
$order_by_property_names{$table_column_names->[0]} = $order_by_property_names{$name}; |
234
|
|
|
|
|
|
|
} else { |
235
|
3
|
|
|
|
|
10
|
$order_by_non_column_data = 1; |
236
|
|
|
|
|
|
|
} |
237
|
|
|
|
|
|
|
} |
238
|
|
|
|
|
|
|
|
239
|
43
|
100
|
|
|
|
135
|
if (@column_data) { |
240
|
37
|
|
|
|
|
95
|
my $additional_order_by_columns = $ds->_select_clause_columns_for_table_property_data(@column_data); |
241
|
|
|
|
|
|
|
|
242
|
|
|
|
|
|
|
# Strip out columns named in the original $order_by_columns list that now appear in the |
243
|
|
|
|
|
|
|
# additional order by list so we don't duplicate columns names, and the additional columns |
244
|
|
|
|
|
|
|
# appear earlier in the list |
245
|
37
|
|
|
|
|
69
|
my %additional_order_by_columns = map { $_ => 1 } @$additional_order_by_columns; |
|
45
|
|
|
|
|
117
|
|
246
|
37
|
|
|
|
|
67
|
my @existing_order_by_columns = grep { ! $additional_order_by_columns{$_} } @$order_by_columns; |
|
37
|
|
|
|
|
110
|
|
247
|
37
|
100
|
|
|
|
81
|
$order_by_columns = [ map { $is_descending{$_} ? '-'. $_ : $_ } ( @$additional_order_by_columns, @existing_order_by_columns ) ]; |
|
76
|
|
|
|
|
245
|
|
248
|
|
|
|
|
|
|
} |
249
|
|
|
|
|
|
|
} |
250
|
688
|
|
|
|
|
2678
|
$self->_order_by_property_names(\%order_by_property_names); |
251
|
688
|
|
|
|
|
1734
|
return ($order_by_columns, $order_by_non_column_data); |
252
|
|
|
|
|
|
|
} |
253
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
|
255
|
|
|
|
|
|
|
sub _init_rdbms { |
256
|
655
|
|
|
655
|
|
1052
|
my $self = shift; |
257
|
655
|
|
|
|
|
2510
|
my $rule_template = $self->rule_template; |
258
|
655
|
|
|
|
|
2180
|
my $ds = $self->data_source; |
259
|
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
# class-based values |
261
|
655
|
|
|
|
|
2009
|
my $class_name = $rule_template->subject_class_name; |
262
|
655
|
|
|
|
|
2703
|
my $class_meta = $class_name->__meta__; |
263
|
655
|
|
|
|
|
2685
|
my $class_data = $ds->_get_class_data_for_loading($class_meta); |
264
|
|
|
|
|
|
|
|
265
|
655
|
|
|
|
|
1216
|
my @parent_class_objects = @{ $class_data->{parent_class_objects} }; |
|
655
|
|
|
|
|
2132
|
|
266
|
655
|
|
|
|
|
970
|
my @all_id_property_names = @{ $class_data->{all_id_property_names} }; |
|
655
|
|
|
|
|
1733
|
|
267
|
655
|
|
|
|
|
832
|
my @id_properties = @{ $class_data->{id_properties} }; |
|
655
|
|
|
|
|
1497
|
|
268
|
|
|
|
|
|
|
|
269
|
|
|
|
|
|
|
#my $first_table_name = $class_data->{first_table_name}; |
270
|
|
|
|
|
|
|
|
271
|
|
|
|
|
|
|
#my $id_property_sorter = $class_data->{id_property_sorter}; |
272
|
|
|
|
|
|
|
#my @lob_column_names = @{ $class_data->{lob_column_names} }; |
273
|
655
|
|
|
|
|
924
|
my @lob_column_positions = @{ $class_data->{lob_column_positions} }; |
|
655
|
|
|
|
|
1605
|
|
274
|
|
|
|
|
|
|
#my $query_config = $class_data->{query_config}; |
275
|
|
|
|
|
|
|
#my $post_process_results_callback = $class_data->{post_process_results_callback}; |
276
|
|
|
|
|
|
|
#my $class_table_name = $class_data->{class_table_name}; |
277
|
|
|
|
|
|
|
|
278
|
|
|
|
|
|
|
# individual template based |
279
|
655
|
|
|
|
|
3065
|
my $hints = $rule_template->hints; |
280
|
655
|
|
|
|
|
1964
|
my %hints = map { $_ => 1 } @$hints; |
|
37
|
|
|
|
|
106
|
|
281
|
655
|
|
|
|
|
1817
|
my $order_by = $rule_template->order_by; |
282
|
655
|
|
|
|
|
1766
|
my $group_by = $rule_template->group_by; |
283
|
655
|
|
|
|
|
2517
|
my $aggregate = $rule_template->aggregate; |
284
|
655
|
|
|
|
|
1736
|
my $recursion_desc = $rule_template->recursion_desc; |
285
|
|
|
|
|
|
|
|
286
|
655
|
|
|
|
|
2432
|
my ($first_table_name, @db_joins) = _resolve_db_joins_for_inheritance($class_meta); |
287
|
|
|
|
|
|
|
|
288
|
655
|
|
|
|
|
3317
|
$self->_db_joins(\@db_joins); |
289
|
655
|
|
|
|
|
2609
|
$self->_obj_joins([]); |
290
|
|
|
|
|
|
|
|
291
|
|
|
|
|
|
|
# an array of arrays, containing $table_name, $column_name, $alias, $object_num |
292
|
|
|
|
|
|
|
# as joins are done we extend this, and then condense it into object fabricators |
293
|
655
|
|
|
|
|
863
|
my @db_property_data = @{ $class_data->{all_table_properties} }; |
|
655
|
|
|
|
|
1762
|
|
294
|
|
|
|
|
|
|
|
295
|
655
|
|
|
|
|
936
|
my %group_by_property_names; |
296
|
655
|
100
|
|
|
|
1778
|
if ($group_by) { |
297
|
|
|
|
|
|
|
# we only pull back columns we're grouping by or aggregating if there is grouping happening |
298
|
13
|
|
|
|
|
35
|
for my $name (@$group_by) { |
299
|
3
|
50
|
|
|
|
23
|
unless ($class_name->can($name)) { |
300
|
0
|
|
|
|
|
0
|
Carp::croak("Cannot group by '$name': Class $class_name has no property/method by that name"); |
301
|
|
|
|
|
|
|
} |
302
|
3
|
|
|
|
|
38
|
$group_by_property_names{$name} = 1; |
303
|
|
|
|
|
|
|
} |
304
|
13
|
|
|
|
|
26
|
for my $data (@db_property_data) { |
305
|
47
|
|
|
|
|
91
|
my $name = $data->[1]->property_name; |
306
|
47
|
100
|
|
|
|
82
|
if ($group_by_property_names{$name}) { |
307
|
1
|
|
|
|
|
2
|
$group_by_property_names{$name} = $data; |
308
|
|
|
|
|
|
|
} |
309
|
|
|
|
|
|
|
} |
310
|
13
|
|
|
|
|
37
|
@db_property_data = grep { ref($_) } values %group_by_property_names; |
|
3
|
|
|
|
|
9
|
|
311
|
|
|
|
|
|
|
} |
312
|
|
|
|
|
|
|
|
313
|
655
|
|
|
|
|
2773
|
my($order_by_columns, $order_by_non_column_data) |
314
|
|
|
|
|
|
|
= $self->_determine_complete_order_by_list($rule_template, $class_data,\@db_property_data); |
315
|
|
|
|
|
|
|
|
316
|
655
|
|
|
|
|
2541
|
$self->_db_column_data(\@db_property_data); |
317
|
655
|
|
|
|
|
2137
|
$self->_group_by_property_names(\%group_by_property_names); |
318
|
|
|
|
|
|
|
|
319
|
|
|
|
|
|
|
# Find out what delegated properties we'll be dealing with |
320
|
655
|
|
|
|
|
962
|
my @sql_filters; |
321
|
|
|
|
|
|
|
my @delegated_properties; |
322
|
655
|
|
|
|
|
929
|
do { |
323
|
|
|
|
|
|
|
my %filters = |
324
|
1059
|
|
|
|
|
2864
|
map { $_ => $rule_template->operator_for($_) } |
325
|
655
|
|
|
|
|
2833
|
grep { substr($_,0,1) ne '-' } |
|
1059
|
|
|
|
|
2551
|
|
326
|
|
|
|
|
|
|
$rule_template->_property_names; |
327
|
|
|
|
|
|
|
|
328
|
655
|
100
|
66
|
|
|
2886
|
unless (@all_id_property_names == 1 && $all_id_property_names[0] eq "id") { |
329
|
630
|
|
|
|
|
1025
|
delete $filters{'id'}; |
330
|
|
|
|
|
|
|
} |
331
|
|
|
|
|
|
|
|
332
|
|
|
|
|
|
|
# Remove the flag for descending/ascending sort |
333
|
655
|
100
|
|
|
|
1973
|
my @order_by_properties = $order_by ? @$order_by : (); |
334
|
655
|
|
|
|
|
1622
|
s/^-|\+// foreach @order_by_properties; |
335
|
|
|
|
|
|
|
|
336
|
655
|
50
|
|
|
|
3625
|
my %properties_involved = map { $_ => 1 } |
|
991
|
100
|
|
|
|
2309
|
|
337
|
|
|
|
|
|
|
keys(%filters), |
338
|
|
|
|
|
|
|
($hints ? @$hints : ()), |
339
|
|
|
|
|
|
|
@order_by_properties, |
340
|
|
|
|
|
|
|
($group_by ? @$group_by : ()); |
341
|
|
|
|
|
|
|
|
342
|
655
|
|
|
|
|
2356
|
my @properties_involved = sort keys(%properties_involved); |
343
|
655
|
|
|
|
|
995
|
my @errors; |
344
|
655
|
|
|
|
|
2061
|
while (my $property_name = shift @properties_involved) { |
345
|
985
|
100
|
|
|
|
2897
|
if (index($property_name,'.') != -1) { |
346
|
18
|
|
|
|
|
31
|
push @delegated_properties, $property_name; |
347
|
18
|
|
|
|
|
52
|
next; |
348
|
|
|
|
|
|
|
} |
349
|
|
|
|
|
|
|
|
350
|
967
|
|
|
|
|
3093
|
my (@pmeta) = $class_meta->property_meta_for_name($property_name); |
351
|
967
|
50
|
|
|
|
2292
|
unless (@pmeta) { |
352
|
0
|
0
|
|
|
|
0
|
if ($class_name->can($property_name)) { |
353
|
|
|
|
|
|
|
# method, not property |
354
|
0
|
|
|
|
|
0
|
next; |
355
|
|
|
|
|
|
|
} |
356
|
|
|
|
|
|
|
else { |
357
|
0
|
|
|
|
|
0
|
push @errors, "Class ".$class_meta->id." has no property or method named '$property_name'"; |
358
|
0
|
|
|
|
|
0
|
next; |
359
|
|
|
|
|
|
|
} |
360
|
|
|
|
|
|
|
} |
361
|
|
|
|
|
|
|
|
362
|
|
|
|
|
|
|
# For each property in this list, go up the inheritance and find the right property |
363
|
|
|
|
|
|
|
# to query on. Give priority to properties that actually have columns |
364
|
|
|
|
|
|
|
FIND_PROPERTY_WITH_COLUMN: |
365
|
967
|
|
|
|
|
1781
|
foreach my $pmeta ( @pmeta ) { |
366
|
967
|
|
|
|
|
33544
|
foreach my $candidate_class ( $class_meta->all_class_metas ) { |
367
|
1276
|
|
|
|
|
4046
|
my $candidate_prop_meta = UR::Object::Property->get(class_name => $candidate_class->class_name, |
368
|
|
|
|
|
|
|
property_name => $property_name); |
369
|
1276
|
100
|
|
|
|
3286
|
next unless $candidate_prop_meta; |
370
|
989
|
100
|
|
|
|
2684
|
if ($candidate_prop_meta->column_name) { |
371
|
850
|
|
|
|
|
1178
|
$pmeta = $candidate_prop_meta; |
372
|
850
|
|
|
|
|
2267
|
next FIND_PROPERTY_WITH_COLUMN; |
373
|
|
|
|
|
|
|
} |
374
|
|
|
|
|
|
|
} |
375
|
|
|
|
|
|
|
} |
376
|
|
|
|
|
|
|
|
377
|
967
|
|
|
|
|
1299
|
my $property = $pmeta[0]; |
378
|
967
|
|
|
|
|
4101
|
my $table_name = $property->class_meta->first_table_name; |
379
|
967
|
|
|
|
|
3011
|
my $operator = $rule_template->operator_for($property_name); |
380
|
967
|
|
|
|
|
2645
|
my $value_position = $rule_template->value_position_for_property_name($property_name); |
381
|
|
|
|
|
|
|
|
382
|
967
|
50
|
|
|
|
4439
|
if ($property->can("expr_sql")) { |
383
|
0
|
0
|
|
|
|
0
|
unless ($table_name) { |
384
|
0
|
|
|
|
|
0
|
$ds->warning_message("Property '$property_name' of class '$class_name' can 'expr_sql' but has no table!"); |
385
|
0
|
|
|
|
|
0
|
next; |
386
|
|
|
|
|
|
|
} |
387
|
0
|
|
|
|
|
0
|
my $expr_sql = $property->expr_sql; |
388
|
0
|
0
|
|
|
|
0
|
if (exists $filters{$property_name}) { |
389
|
0
|
|
|
|
|
0
|
my @coercion = $self->data_source->cast_for_data_conversion( |
390
|
|
|
|
|
|
|
$property->_data_type_as_class_name, |
391
|
|
|
|
|
|
|
'UR::Value::String', # We can't know here what the type should be |
392
|
|
|
|
|
|
|
$operator, |
393
|
|
|
|
|
|
|
'where'); |
394
|
0
|
|
|
|
|
0
|
push @sql_filters, |
395
|
|
|
|
|
|
|
$table_name => { |
396
|
|
|
|
|
|
|
# cheap hack of prefixing with a whitespace differentiates |
397
|
|
|
|
|
|
|
# from a regular column below |
398
|
|
|
|
|
|
|
" " . $expr_sql => { |
399
|
|
|
|
|
|
|
operator => $operator, |
400
|
|
|
|
|
|
|
value_position => $value_position, |
401
|
|
|
|
|
|
|
left_coercion => $coercion[0], |
402
|
|
|
|
|
|
|
right_coercion => $coercion[1], |
403
|
|
|
|
|
|
|
} |
404
|
|
|
|
|
|
|
}; |
405
|
|
|
|
|
|
|
} |
406
|
0
|
|
|
|
|
0
|
next; |
407
|
|
|
|
|
|
|
} |
408
|
|
|
|
|
|
|
|
409
|
|
|
|
|
|
|
# If the property is calculate and has a calculate_from list, add the |
410
|
|
|
|
|
|
|
# calculate_from things to the internal hints list, but not the template |
411
|
967
|
100
|
66
|
|
|
28878
|
if ($property->is_calculated and $property->calculate_from) { |
412
|
9
|
|
|
|
|
31
|
my $calculate_from = $property->calculate_from; |
413
|
9
|
|
|
|
|
22
|
push @properties_involved, @$calculate_from; |
414
|
9
|
|
|
|
|
17
|
push @$hints, @$calculate_from; |
415
|
9
|
|
|
|
|
39
|
$hints{$_} = 1 foreach @$calculate_from; |
416
|
|
|
|
|
|
|
} |
417
|
|
|
|
|
|
|
|
418
|
967
|
100
|
100
|
|
|
6421
|
if (exists($filters{$property_name}) and $filters{$property_name} eq 'isa') { |
|
|
100
|
66
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
419
|
|
|
|
|
|
|
# RDBMS databases can't do 'isa' |
420
|
2
|
|
|
|
|
10
|
$self->needs_further_boolexpr_evaluation_after_loading(1); |
421
|
2
|
|
|
|
|
8
|
next; |
422
|
|
|
|
|
|
|
} |
423
|
|
|
|
|
|
|
elsif (my $column_name = $property->column_name) { |
424
|
|
|
|
|
|
|
# normal column: filter on it |
425
|
848
|
50
|
|
|
|
1953
|
unless ($table_name) { |
426
|
0
|
|
|
|
|
0
|
$ds->warning_message("Property '$property_name' of class '$class_name' has column '$column_name' but has no table!"); |
427
|
0
|
|
|
|
|
0
|
next; |
428
|
|
|
|
|
|
|
} |
429
|
848
|
100
|
|
|
|
2464
|
if (exists $filters{$property_name}) { |
430
|
809
|
|
|
|
|
2344
|
my @coercion = $self->data_source->cast_for_data_conversion( |
431
|
|
|
|
|
|
|
$property->_data_type_as_class_name, |
432
|
|
|
|
|
|
|
'UR::Value::String', # We can't know here what the type should be |
433
|
|
|
|
|
|
|
$operator, |
434
|
|
|
|
|
|
|
'where'); |
435
|
809
|
|
|
|
|
6596
|
push @sql_filters, |
436
|
|
|
|
|
|
|
$table_name => { |
437
|
|
|
|
|
|
|
$column_name => { |
438
|
|
|
|
|
|
|
operator => $operator, |
439
|
|
|
|
|
|
|
value_position => $value_position, |
440
|
|
|
|
|
|
|
left_coercion => $coercion[0], |
441
|
|
|
|
|
|
|
right_coercion => $coercion[1], |
442
|
|
|
|
|
|
|
} |
443
|
|
|
|
|
|
|
}; |
444
|
|
|
|
|
|
|
} |
445
|
|
|
|
|
|
|
} |
446
|
|
|
|
|
|
|
elsif ($property->is_delegated) { |
447
|
76
|
|
|
|
|
244
|
push @delegated_properties, $property->property_name; |
448
|
|
|
|
|
|
|
} |
449
|
|
|
|
|
|
|
elsif ( ! exists($hints{$property_name}) or exists($filters{$property_name}) ) { |
450
|
33
|
|
|
|
|
151
|
$self->needs_further_boolexpr_evaluation_after_loading(1); |
451
|
|
|
|
|
|
|
} |
452
|
|
|
|
|
|
|
else { |
453
|
8
|
|
|
|
|
30
|
next; |
454
|
|
|
|
|
|
|
} |
455
|
|
|
|
|
|
|
|
456
|
|
|
|
|
|
|
} # end of properties in the expression which control the query content |
457
|
|
|
|
|
|
|
|
458
|
655
|
50
|
|
|
|
2517
|
if (@errors) { |
459
|
0
|
|
|
|
|
0
|
my $class_name = $class_meta->class_name; |
460
|
0
|
|
|
|
|
0
|
$ds->error_message("ERRORS PROCESSING PARAMTERS: (" . join("\n", @errors) . ") used to generate SQL for $class_name!"); |
461
|
|
|
|
|
|
|
#print Data::Dumper::Dumper($rule_template); |
462
|
0
|
|
|
|
|
0
|
Carp::croak("Can't continue"); |
463
|
|
|
|
|
|
|
} |
464
|
|
|
|
|
|
|
}; |
465
|
|
|
|
|
|
|
|
466
|
655
|
|
|
|
|
1138
|
my $object_num = 0; |
467
|
655
|
|
|
|
|
2879
|
$self->_alias_count(0); |
468
|
|
|
|
|
|
|
|
469
|
655
|
|
|
|
|
937
|
my %hints_included; |
470
|
|
|
|
|
|
|
my @select_hint; |
471
|
|
|
|
|
|
|
|
472
|
|
|
|
|
|
|
# FIXME - this needs to be broken out into delegated-property-join-resolver |
473
|
|
|
|
|
|
|
# and inheritance-join-resolver methods that can be called recursively. |
474
|
|
|
|
|
|
|
# It would better encapsulate what's going on and avoid bugs with complicated |
475
|
|
|
|
|
|
|
# get()s |
476
|
|
|
|
|
|
|
|
477
|
|
|
|
|
|
|
# one iteration per target value involved in the query, |
478
|
|
|
|
|
|
|
# including values needed for filtering, ordering, grouping, and hints (selecting more) |
479
|
|
|
|
|
|
|
# these "properties" may be a single property name or an ad-hoc "chain" |
480
|
|
|
|
|
|
|
DELEGATED_PROPERTY: |
481
|
655
|
|
|
|
|
1897
|
for my $delegated_property (sort @delegated_properties) { |
482
|
94
|
|
|
|
|
190
|
my $property_name = $delegated_property; |
483
|
94
|
|
66
|
|
|
350
|
my $delegation_chain_data = $self->_delegation_chain_data || $self->_delegation_chain_data({}); |
484
|
94
|
|
|
|
|
365
|
$delegation_chain_data->{"__all__"}{table_alias} = {}; |
485
|
94
|
|
|
|
|
342
|
$delegation_chain_data->{"__all__"}{class_alias} = { $first_table_name => $class_meta }; |
486
|
|
|
|
|
|
|
|
487
|
94
|
|
|
|
|
444
|
my ($final_accessor, $is_optional, @joins) = _resolve_object_join_data_for_property_chain($rule_template,$property_name,$property_name); |
488
|
|
|
|
|
|
|
|
489
|
|
|
|
|
|
|
# when there is no "final_accessor" it often means we have an object-accessor in a hint |
490
|
|
|
|
|
|
|
# we want that to go through the join process, and only be left out at filter construction time |
491
|
|
|
|
|
|
|
#unless ($final_accessor) { |
492
|
|
|
|
|
|
|
#$self->needs_further_boolexpr_evaluation_after_loading(1); |
493
|
|
|
|
|
|
|
#next; |
494
|
|
|
|
|
|
|
#} |
495
|
|
|
|
|
|
|
|
496
|
|
|
|
|
|
|
# this is gathered here and used below, but previously was gathered internally to the methods which take it |
497
|
|
|
|
|
|
|
# since it is no longer needed directly in this method it might be refactored into the places which use it |
498
|
94
|
|
|
|
|
154
|
my %ds_for_class; |
499
|
94
|
|
|
|
|
191
|
for my $join (@joins) { |
500
|
223
|
|
|
|
|
767
|
my $source_class_object = $join->{'source_class'}->__meta__; |
501
|
223
|
|
|
|
|
704
|
my ($source_data_source) = UR::Context->resolve_data_sources_for_class_meta_and_rule($source_class_object, $rule_template); |
502
|
223
|
|
|
|
|
413
|
$ds_for_class{$join->{'source_class'}} = $source_data_source; |
503
|
|
|
|
|
|
|
|
504
|
223
|
|
|
|
|
898
|
my $foreign_class_object = $join->{'foreign_class'}->__meta__; |
505
|
223
|
|
|
|
|
543
|
my ($foreign_data_source) = UR::Context->resolve_data_sources_for_class_meta_and_rule($foreign_class_object, $rule_template); |
506
|
223
|
|
|
|
|
537
|
$ds_for_class{$join->{'foreign_class'}} = $foreign_data_source; |
507
|
|
|
|
|
|
|
} |
508
|
|
|
|
|
|
|
|
509
|
|
|
|
|
|
|
|
510
|
|
|
|
|
|
|
# Splice out joins that go through a UR::Value class and back out to the DB, since UR::Value-types |
511
|
|
|
|
|
|
|
# don't get stored in the DB |
512
|
|
|
|
|
|
|
# TODO: move this into the join creation logic |
513
|
94
|
|
|
|
|
389
|
for (my $i = 0; $i < @joins; $i++) { |
514
|
218
|
100
|
100
|
|
|
1429
|
if ( |
|
|
|
66
|
|
|
|
|
515
|
|
|
|
|
|
|
$i < $#joins |
516
|
|
|
|
|
|
|
and |
517
|
|
|
|
|
|
|
( |
518
|
|
|
|
|
|
|
# db -> UR::Value -> db : shortcut |
519
|
|
|
|
|
|
|
$joins[$i]->{'foreign_class'}->isa('UR::Value') |
520
|
|
|
|
|
|
|
and $joins[$i+1]->{'source_class'}->isa('UR::Value') |
521
|
|
|
|
|
|
|
#and $joins[$i]->{'foreign_class'}->isa($joins[$i+1]->{'source_class'}) ## remove this? |
522
|
|
|
|
|
|
|
) |
523
|
|
|
|
|
|
|
) { |
524
|
|
|
|
|
|
|
my $fixed_join = UR::Object::Join->_get_or_define( |
525
|
|
|
|
|
|
|
source_class => $joins[$i]->{'source_class'}, |
526
|
|
|
|
|
|
|
source_property_names => $joins[$i]->{'source_property_names'}, |
527
|
|
|
|
|
|
|
foreign_class => $joins[$i+1]->{'foreign_class'}, |
528
|
|
|
|
|
|
|
foreign_property_names => $joins[$i+1]->{'foreign_property_names'}, |
529
|
|
|
|
|
|
|
is_optional => $joins[$i]->{'is_optional'}, |
530
|
5
|
|
|
|
|
54
|
id => $joins[$i]->{id} . "->" . $joins[$i+1]->{id}); |
531
|
5
|
100
|
|
|
|
27
|
if ($joins[$i+1]->{where}) { |
532
|
|
|
|
|
|
|
# If there's a where involved, it will always be on the second thing, |
533
|
|
|
|
|
|
|
# where the foreign_class is NOT a UR::Value |
534
|
1
|
|
|
|
|
3
|
$fixed_join->{where} = $joins[$i+1]->{where}; |
535
|
|
|
|
|
|
|
} |
536
|
5
|
|
|
|
|
22
|
splice(@joins, $i, 2, $fixed_join); |
537
|
|
|
|
|
|
|
} |
538
|
|
|
|
|
|
|
} |
539
|
|
|
|
|
|
|
|
540
|
94
|
100
|
66
|
|
|
771
|
if (@joins and $joins[-1]{foreign_class}->isa("UR::Value")) { |
541
|
|
|
|
|
|
|
# the final join in a chain is often the link between a primitive value |
542
|
|
|
|
|
|
|
# and the UR::Value subclass into which it falls ...irrelevent for db joins |
543
|
86
|
|
|
|
|
359
|
$final_accessor = $joins[-1]->source_property_names->[0]; |
544
|
86
|
|
|
|
|
149
|
pop @joins; |
545
|
86
|
50
|
|
|
|
234
|
next DELEGATED_PROPERTY unless @joins; |
546
|
|
|
|
|
|
|
} |
547
|
|
|
|
|
|
|
|
548
|
94
|
|
|
|
|
155
|
my $last_class_object_excluding_inherited_joins; |
549
|
|
|
|
|
|
|
my $alias_for_property_value; |
550
|
|
|
|
|
|
|
|
551
|
|
|
|
|
|
|
# one iteration per table between the start table and target |
552
|
94
|
|
|
|
|
317
|
while (my $object_join = shift @joins) { |
553
|
132
|
|
|
|
|
189
|
$object_num++; |
554
|
132
|
|
|
|
|
226
|
my @joins_for_object = ($object_join); |
555
|
|
|
|
|
|
|
|
556
|
|
|
|
|
|
|
# one iteration per layer of inheritance for this object |
557
|
|
|
|
|
|
|
# or per case of a join having additional filtering |
558
|
132
|
|
|
|
|
164
|
my $current_inheritance_depth_for_this_target_join = 0; |
559
|
132
|
|
|
|
|
336
|
while (my $join = shift @joins_for_object) { |
560
|
|
|
|
|
|
|
|
561
|
148
|
|
|
|
|
261
|
my $where = $join->{where}; |
562
|
|
|
|
|
|
|
|
563
|
148
|
|
|
|
|
173
|
$current_inheritance_depth_for_this_target_join++; |
564
|
|
|
|
|
|
|
|
565
|
148
|
|
|
|
|
233
|
my $foreign_class_name = $join->{foreign_class}; |
566
|
148
|
|
33
|
|
|
719
|
my $foreign_class_object = $join->{'foreign_class_meta'} || $foreign_class_name->__meta__; |
567
|
|
|
|
|
|
|
|
568
|
148
|
100
|
66
|
|
|
1017
|
if ($foreign_class_object->join_hint and !($hints_included{$foreign_class_name}++)) { |
569
|
1
|
|
|
|
|
4
|
push @select_hint, $foreign_class_object->join_hint; |
570
|
|
|
|
|
|
|
} |
571
|
|
|
|
|
|
|
|
572
|
148
|
50
|
|
|
|
412
|
if (not exists $ds_for_class{$foreign_class_name}) { |
573
|
|
|
|
|
|
|
# error: we should have at least a key with an empty value if we tried to find the ds |
574
|
0
|
|
|
|
|
0
|
die "no data source key for $foreign_class_name when adding a join?" |
575
|
|
|
|
|
|
|
} |
576
|
|
|
|
|
|
|
|
577
|
148
|
|
|
|
|
228
|
my $ds = $ds_for_class{$foreign_class_name}; |
578
|
|
|
|
|
|
|
|
579
|
148
|
50
|
|
|
|
360
|
if (not $ds) { |
580
|
|
|
|
|
|
|
# no ds for the next piece of data: we will have to resolve this on the client side |
581
|
|
|
|
|
|
|
# this is where things may get slow if the query is insufficiently filtered |
582
|
0
|
|
|
|
|
0
|
$self->needs_further_boolexpr_evaluation_after_loading(1); |
583
|
0
|
|
|
|
|
0
|
next DELEGATED_PROPERTY; |
584
|
|
|
|
|
|
|
} |
585
|
|
|
|
|
|
|
|
586
|
|
|
|
|
|
|
my $alias = $self->_add_join( |
587
|
|
|
|
|
|
|
$delegated_property, |
588
|
|
|
|
|
|
|
$join, |
589
|
|
|
|
|
|
|
$object_num, |
590
|
|
|
|
|
|
|
$is_optional, |
591
|
|
|
|
|
|
|
$final_accessor, |
592
|
148
|
|
|
|
|
617
|
$ds_for_class{$foreign_class_name}, |
593
|
|
|
|
|
|
|
); |
594
|
|
|
|
|
|
|
|
595
|
148
|
100
|
|
|
|
376
|
if (not $alias) { |
596
|
|
|
|
|
|
|
# unable to add a join for another reason |
597
|
|
|
|
|
|
|
# TODO: is the above the only valid case of a join being impossible? |
598
|
|
|
|
|
|
|
# Can we remove this? |
599
|
5
|
|
|
|
|
18
|
$self->needs_further_boolexpr_evaluation_after_loading(1); |
600
|
5
|
|
|
|
|
26
|
next DELEGATED_PROPERTY; |
601
|
|
|
|
|
|
|
} |
602
|
|
|
|
|
|
|
|
603
|
|
|
|
|
|
|
# set these for after all of the joins are done |
604
|
143
|
|
|
|
|
205
|
my $last_class_name = $foreign_class_name; |
605
|
143
|
|
|
|
|
173
|
my $last_class_object = $foreign_class_object; |
606
|
|
|
|
|
|
|
|
607
|
|
|
|
|
|
|
# on the first iteration, we figure out the remaining inherited iterations |
608
|
|
|
|
|
|
|
# if there is inheritance to do, unshift those onto the stack ahead of other things |
609
|
143
|
100
|
|
|
|
358
|
if ($current_inheritance_depth_for_this_target_join == 1) { |
610
|
127
|
100
|
100
|
|
|
556
|
if ($final_accessor and $last_class_object->property_meta_for_name($final_accessor)) { |
611
|
96
|
|
|
|
|
144
|
$last_class_object_excluding_inherited_joins = $last_class_object; |
612
|
|
|
|
|
|
|
} |
613
|
127
|
|
|
|
|
398
|
my @parents = grep { $_->table_name } $foreign_class_object->ancestry_class_metas; |
|
270
|
|
|
|
|
586
|
|
614
|
127
|
100
|
|
|
|
392
|
if (@parents) { |
615
|
16
|
|
|
|
|
53
|
my @last_id_property_names = $foreign_class_object->id_property_names; |
616
|
16
|
|
|
|
|
35
|
for my $parent (@parents) { |
617
|
16
|
|
|
|
|
60
|
my @parent_id_property_names = $parent->id_property_names; |
618
|
16
|
50
|
|
|
|
45
|
die if @parent_id_property_names > 1; |
619
|
16
|
|
|
|
|
45
|
my $parent_join_foreign_class_name = $parent->class_name; |
620
|
16
|
|
|
|
|
147
|
my $inheritance_join = UR::Object::Join->_get_or_define( |
621
|
|
|
|
|
|
|
source_class => $last_class_name, |
622
|
|
|
|
|
|
|
source_property_names => [@last_id_property_names], # we change content below |
623
|
|
|
|
|
|
|
foreign_class => $parent_join_foreign_class_name, |
624
|
|
|
|
|
|
|
foreign_property_names => \@parent_id_property_names, |
625
|
|
|
|
|
|
|
is_optional => $is_optional, |
626
|
|
|
|
|
|
|
id => "${last_class_name}::" . join(',',@last_id_property_names), |
627
|
|
|
|
|
|
|
); |
628
|
16
|
|
|
|
|
33
|
unshift @joins_for_object, $inheritance_join; |
629
|
16
|
|
|
|
|
35
|
@last_id_property_names = @parent_id_property_names; |
630
|
16
|
|
|
|
|
23
|
$last_class_name = $foreign_class_name; |
631
|
|
|
|
|
|
|
|
632
|
16
|
|
|
|
|
66
|
my $foreign_class_object = $parent_join_foreign_class_name->__meta__; |
633
|
16
|
|
|
|
|
61
|
my ($foreign_data_source) = UR::Context->resolve_data_sources_for_class_meta_and_rule($foreign_class_object, $rule_template); |
634
|
16
|
|
|
|
|
48
|
$ds_for_class{$parent_join_foreign_class_name} = $foreign_data_source; |
635
|
|
|
|
|
|
|
} |
636
|
16
|
|
|
|
|
61
|
next; |
637
|
|
|
|
|
|
|
} |
638
|
|
|
|
|
|
|
} |
639
|
|
|
|
|
|
|
|
640
|
127
|
50
|
66
|
|
|
872
|
if (!@joins and not $alias_for_property_value) { |
641
|
|
|
|
|
|
|
# we are out of joins for this delegated property |
642
|
|
|
|
|
|
|
# setting $alias_for_property_value helps map to exactly where we do real filter/order/etc. |
643
|
89
|
|
|
|
|
347
|
my $foreign_class_loading_data = $ds->_get_class_data_for_loading($foreign_class_object); |
644
|
89
|
100
|
100
|
|
|
388
|
if ($final_accessor and |
645
|
281
|
|
|
|
|
549
|
grep { $_->[1]->property_name eq $final_accessor } @{ $foreign_class_loading_data->{direct_table_properties} } |
|
88
|
|
|
|
|
228
|
|
646
|
|
|
|
|
|
|
) { |
647
|
77
|
|
|
|
|
908
|
$alias_for_property_value = $alias; |
648
|
|
|
|
|
|
|
#print "found alias for $property_name on $foreign_class_name: $alias\n"; |
649
|
|
|
|
|
|
|
} |
650
|
|
|
|
|
|
|
else { |
651
|
|
|
|
|
|
|
# The thing we're joining to isn't a database-backed column (maybe calculated?) |
652
|
12
|
|
|
|
|
58
|
$self->needs_further_boolexpr_evaluation_after_loading(1); |
653
|
12
|
|
|
|
|
129
|
next DELEGATED_PROPERTY; |
654
|
|
|
|
|
|
|
} |
655
|
|
|
|
|
|
|
} |
656
|
|
|
|
|
|
|
|
657
|
|
|
|
|
|
|
} # next join in the inheritance for this object |
658
|
|
|
|
|
|
|
|
659
|
|
|
|
|
|
|
} # next join across objects from the query subject to the delegated property target |
660
|
|
|
|
|
|
|
|
661
|
|
|
|
|
|
|
# done adding any new joins for this delegated property/property-chain |
662
|
|
|
|
|
|
|
|
663
|
|
|
|
|
|
|
# now see if anything in the where-clause needs to filter on the item joined-to |
664
|
77
|
|
|
|
|
381
|
my $value_position = $rule_template->value_position_for_property_name($property_name); |
665
|
77
|
100
|
|
|
|
279
|
if (defined $value_position) { |
666
|
|
|
|
|
|
|
# this property _is_ used to filter results |
667
|
62
|
50
|
|
|
|
162
|
if (not $final_accessor) { |
668
|
|
|
|
|
|
|
# on the client side :( |
669
|
0
|
|
|
|
|
0
|
$self->needs_further_boolexpr_evaluation_after_loading(1); |
670
|
0
|
|
|
|
|
0
|
next; |
671
|
|
|
|
|
|
|
} |
672
|
|
|
|
|
|
|
else { |
673
|
|
|
|
|
|
|
# at the database level :) |
674
|
62
|
|
|
|
|
218
|
my $final_accessor_property_meta = $last_class_object_excluding_inherited_joins->property_meta_for_name($final_accessor); |
675
|
62
|
50
|
|
|
|
189
|
unless ($final_accessor_property_meta) { |
676
|
0
|
|
|
|
|
0
|
Carp::croak("No property metadata for property named '$final_accessor' in class " |
677
|
|
|
|
|
|
|
. $last_class_object_excluding_inherited_joins->class_name |
678
|
|
|
|
|
|
|
. " while resolving joins for property '" . $delegated_property->property_name . "' in class " |
679
|
|
|
|
|
|
|
. $delegated_property->class_name); |
680
|
|
|
|
|
|
|
} |
681
|
|
|
|
|
|
|
|
682
|
62
|
|
|
|
|
105
|
my $sql_lvalue; |
683
|
62
|
50
|
|
|
|
194
|
if ($final_accessor_property_meta->is_calculated) { |
684
|
0
|
|
|
|
|
0
|
$sql_lvalue = $final_accessor_property_meta->calculate_sql; |
685
|
0
|
0
|
|
|
|
0
|
unless (defined($sql_lvalue)) { |
686
|
0
|
|
|
|
|
0
|
$self->needs_further_boolexpr_evaluation_after_loading(1); |
687
|
0
|
|
|
|
|
0
|
next; |
688
|
|
|
|
|
|
|
} |
689
|
|
|
|
|
|
|
} |
690
|
|
|
|
|
|
|
else { |
691
|
62
|
|
|
|
|
177
|
$sql_lvalue = $final_accessor_property_meta->column_name; |
692
|
62
|
50
|
|
|
|
192
|
unless (defined($sql_lvalue)) { |
693
|
0
|
|
|
|
|
0
|
Carp::confess("No column name set for non-delegated/calculated property $property_name of $class_name"); |
694
|
|
|
|
|
|
|
} |
695
|
|
|
|
|
|
|
} |
696
|
|
|
|
|
|
|
|
697
|
62
|
|
|
|
|
232
|
my $operator = $rule_template->operator_for($property_name); |
698
|
|
|
|
|
|
|
|
699
|
62
|
50
|
|
|
|
194
|
unless ($alias_for_property_value) { |
700
|
0
|
|
|
|
|
0
|
die "No alias found for $property_name?!"; |
701
|
|
|
|
|
|
|
} |
702
|
|
|
|
|
|
|
|
703
|
62
|
|
|
|
|
246
|
my @coercion = $self->data_source->cast_for_data_conversion( |
704
|
|
|
|
|
|
|
$final_accessor_property_meta->_data_type_as_class_name, |
705
|
|
|
|
|
|
|
'UR::Value::String', # We can't know here what the type should be |
706
|
|
|
|
|
|
|
$operator, |
707
|
|
|
|
|
|
|
'where'); |
708
|
|
|
|
|
|
|
|
709
|
62
|
|
|
|
|
562
|
push @sql_filters, |
710
|
|
|
|
|
|
|
$alias_for_property_value => { |
711
|
|
|
|
|
|
|
$sql_lvalue => { |
712
|
|
|
|
|
|
|
operator => $operator, |
713
|
|
|
|
|
|
|
value_position => $value_position, |
714
|
|
|
|
|
|
|
left_coercion => $coercion[0], |
715
|
|
|
|
|
|
|
right_coercion => $coercion[1], |
716
|
|
|
|
|
|
|
} |
717
|
|
|
|
|
|
|
}; |
718
|
|
|
|
|
|
|
} |
719
|
|
|
|
|
|
|
} |
720
|
|
|
|
|
|
|
|
721
|
|
|
|
|
|
|
} # next delegated property |
722
|
|
|
|
|
|
|
|
723
|
|
|
|
|
|
|
# the columns to query |
724
|
655
|
|
|
|
|
1985
|
my $db_property_data = $self->_db_column_data; |
725
|
|
|
|
|
|
|
|
726
|
|
|
|
|
|
|
# the following two sets of variables hold the net result of the logic |
727
|
655
|
|
|
|
|
942
|
my $select_clause; |
728
|
|
|
|
|
|
|
my $from_clause; |
729
|
0
|
|
|
|
|
0
|
my $connect_by_clause; |
730
|
0
|
|
|
|
|
0
|
my $group_by_clause; |
731
|
|
|
|
|
|
|
|
732
|
|
|
|
|
|
|
# Build the SELECT clause explicitly. |
733
|
655
|
|
|
|
|
3442
|
$select_clause = $ds->_select_clause_for_table_property_data(@$db_property_data); |
734
|
|
|
|
|
|
|
|
735
|
|
|
|
|
|
|
# Oracle places group_by in a comment in the select |
736
|
655
|
100
|
|
|
|
4834
|
unshift(@select_hint, $class_meta->select_hint) if $class_meta->select_hint; |
737
|
|
|
|
|
|
|
|
738
|
|
|
|
|
|
|
# Build the FROM clause base. |
739
|
|
|
|
|
|
|
# Add joins to the from clause as necessary, then |
740
|
655
|
50
|
|
|
|
2244
|
$from_clause = (defined $first_table_name ? "$first_table_name" : ''); |
741
|
|
|
|
|
|
|
|
742
|
655
|
|
|
|
|
884
|
my $cnt = 0; |
743
|
655
|
|
|
|
|
890
|
my @sql_params; |
744
|
655
|
|
|
|
|
862
|
my @sql_joins = @{ $self->_db_joins }; |
|
655
|
|
|
|
|
1805
|
|
745
|
655
|
|
|
|
|
1955
|
while (@sql_joins) { |
746
|
185
|
|
|
|
|
281
|
my $table_name = shift (@sql_joins); |
747
|
185
|
|
|
|
|
251
|
my $condition = shift (@sql_joins); |
748
|
185
|
|
|
|
|
815
|
my ($table_alias) = ($table_name =~ /(\S+)\s*$/s); |
749
|
|
|
|
|
|
|
|
750
|
185
|
|
|
|
|
218
|
my $join_type; |
751
|
185
|
100
|
|
|
|
882
|
if ($condition->{-is_required}) { |
752
|
48
|
|
|
|
|
77
|
$join_type = 'INNER'; |
753
|
|
|
|
|
|
|
} |
754
|
|
|
|
|
|
|
else { |
755
|
137
|
|
|
|
|
172
|
$join_type = 'LEFT'; |
756
|
|
|
|
|
|
|
} |
757
|
|
|
|
|
|
|
|
758
|
185
|
|
|
|
|
503
|
$from_clause .= "\n$join_type join " . $table_name . " on "; |
759
|
|
|
|
|
|
|
# Restart the counter on each join for the from clause, |
760
|
|
|
|
|
|
|
# but for the where clause keep counting w/o reset. |
761
|
185
|
|
|
|
|
205
|
$cnt = 0; |
762
|
|
|
|
|
|
|
|
763
|
185
|
|
|
|
|
458
|
for my $column_name (keys %$condition) { |
764
|
272
|
100
|
|
|
|
742
|
next if substr($column_name,0,1) eq '-'; |
765
|
|
|
|
|
|
|
|
766
|
224
|
|
|
|
|
312
|
my $linkage_data = $condition->{$column_name}; |
767
|
224
|
50
|
|
|
|
645
|
my $expr_sql = (substr($column_name,0,1) eq " " ? $column_name : "${table_alias}.${column_name}"); |
768
|
|
|
|
|
|
|
my ($operator, $value_position, $value, $link_table_name, $link_column_name, $left_coercion, $right_coercion) |
769
|
224
|
|
|
|
|
632
|
= @$linkage_data{qw/operator value_position value link_table_name link_column_name left_coercion right_coercion/}; |
770
|
|
|
|
|
|
|
|
771
|
224
|
100
|
|
|
|
753
|
$expr_sql = sprintf($right_coercion, $expr_sql) if ($right_coercion); |
772
|
|
|
|
|
|
|
|
773
|
224
|
100
|
|
|
|
482
|
$from_clause .= "\n and " if ($cnt++); |
774
|
|
|
|
|
|
|
|
775
|
224
|
100
|
66
|
|
|
923
|
if ($link_table_name and $link_column_name) { |
|
|
50
|
|
|
|
|
|
776
|
|
|
|
|
|
|
# the linkage data is a join specifier |
777
|
185
|
|
|
|
|
333
|
my $link_sql = "${link_table_name}.${link_column_name}"; |
778
|
185
|
50
|
|
|
|
518
|
$link_sql = sprintf($left_coercion, $link_sql) if ($left_coercion); |
779
|
185
|
|
|
|
|
709
|
$from_clause .= "$link_sql = $expr_sql"; |
780
|
|
|
|
|
|
|
} |
781
|
|
|
|
|
|
|
elsif (defined $value_position) { |
782
|
0
|
|
|
|
|
0
|
Carp::croak("Joins cannot use variable values currently!"); |
783
|
|
|
|
|
|
|
} |
784
|
|
|
|
|
|
|
else { |
785
|
39
|
|
|
|
|
240
|
my ($more_sql, @more_params) = $ds->_extend_sql_for_column_operator_and_value($expr_sql, $operator, $value); |
786
|
39
|
50
|
|
|
|
87
|
if ($more_sql) { |
787
|
39
|
|
|
|
|
61
|
$from_clause .= $more_sql; |
788
|
39
|
|
|
|
|
123
|
push @sql_params, @more_params; |
789
|
|
|
|
|
|
|
} |
790
|
|
|
|
|
|
|
else { |
791
|
|
|
|
|
|
|
# error |
792
|
0
|
|
|
|
|
0
|
return; |
793
|
|
|
|
|
|
|
} |
794
|
|
|
|
|
|
|
} |
795
|
|
|
|
|
|
|
} # next column |
796
|
|
|
|
|
|
|
} # next db join |
797
|
|
|
|
|
|
|
|
798
|
|
|
|
|
|
|
# build the WHERE clause by making a data structure which will be parsed outside of this module |
799
|
|
|
|
|
|
|
# special handling of different size lists, and NULLs, make a completely reusable SQL template very hard. |
800
|
655
|
|
|
|
|
1109
|
my @filter_specs; |
801
|
655
|
|
|
|
|
1752
|
while (@sql_filters) { |
802
|
871
|
|
|
|
|
1222
|
my $table_name = shift (@sql_filters); |
803
|
871
|
|
|
|
|
1168
|
my $condition = shift (@sql_filters); |
804
|
871
|
|
|
|
|
3155
|
my ($table_alias) = ($table_name =~ /(\S+)\s*$/s); |
805
|
|
|
|
|
|
|
|
806
|
871
|
|
|
|
|
2349
|
for my $column_name (keys %$condition) { |
807
|
871
|
|
|
|
|
1323
|
my $linkage_data = $condition->{$column_name}; |
808
|
871
|
50
|
|
|
|
2981
|
my $expr_sql = (substr($column_name,0,1) eq " " ? $column_name : "${table_alias}.${column_name}"); |
809
|
|
|
|
|
|
|
my ($operator, $value_position, $value, $link_table_name, $link_column_name, $left_coercion, $right_coercion) |
810
|
871
|
|
|
|
|
2522
|
= @$linkage_data{qw/operator value_position value link_table_name |
811
|
|
|
|
|
|
|
link_column_name left_coercion right_coercion/}; |
812
|
|
|
|
|
|
|
|
813
|
871
|
50
|
33
|
|
|
2510
|
if ($link_table_name and $link_column_name) { |
814
|
|
|
|
|
|
|
# the linkage data is a join specifier |
815
|
0
|
|
|
|
|
0
|
Carp::confess("explicit column linkage in where clause?"); |
816
|
|
|
|
|
|
|
#$sql .= "${link_table_name}.${link_column_name} = $expr_sql"; |
817
|
|
|
|
|
|
|
} |
818
|
|
|
|
|
|
|
else { |
819
|
|
|
|
|
|
|
# the linkage data is a value position from the @values list |
820
|
871
|
50
|
|
|
|
1853
|
unless (defined $value_position) { |
821
|
0
|
|
|
|
|
0
|
Carp::confess("No value position for $column_name in query!"); |
822
|
|
|
|
|
|
|
} |
823
|
|
|
|
|
|
|
|
824
|
871
|
|
|
|
|
3053
|
$expr_sql = sprintf($left_coercion, $expr_sql); |
825
|
|
|
|
|
|
|
|
826
|
871
|
|
|
|
|
4384
|
push @filter_specs, [$expr_sql, $operator, $value_position]; |
827
|
|
|
|
|
|
|
} |
828
|
|
|
|
|
|
|
} # next column |
829
|
|
|
|
|
|
|
} # next db filter |
830
|
|
|
|
|
|
|
|
831
|
655
|
|
|
|
|
1125
|
$connect_by_clause = ''; |
832
|
655
|
|
|
|
|
897
|
my $recurse_resolution_by_iteration = 0; |
833
|
655
|
100
|
|
|
|
1709
|
if ($recursion_desc) { |
834
|
5
|
50
|
|
|
|
16
|
unless (ref($recursion_desc) eq 'ARRAY') { |
835
|
0
|
|
|
|
|
0
|
Carp::croak("Recursion description must be an arrayref with exactly 2 items"); |
836
|
|
|
|
|
|
|
} |
837
|
5
|
50
|
|
|
|
14
|
if (@$recursion_desc != 2) { |
838
|
0
|
|
|
|
|
0
|
Carp::croak("Recursion description must contain exactly 2 items; got ".scalar(@$recursion_desc) |
839
|
|
|
|
|
|
|
. ': ' . join(', ',@$recursion_desc)); |
840
|
|
|
|
|
|
|
} |
841
|
|
|
|
|
|
|
|
842
|
|
|
|
|
|
|
# Oracle supports "connect by" queries. |
843
|
5
|
50
|
|
|
|
18
|
if ($ds->does_support_recursive_queries eq 'connect by') { |
844
|
0
|
|
|
|
|
0
|
my ($this,$prior) = @{ $recursion_desc }; |
|
0
|
|
|
|
|
0
|
|
845
|
|
|
|
|
|
|
|
846
|
0
|
|
|
|
|
0
|
my $this_property_meta = $class_meta->property_meta_for_name($this); |
847
|
0
|
0
|
|
|
|
0
|
unless ($this_property_meta) { |
848
|
0
|
|
|
|
|
0
|
Carp::croak("Class ".$class_meta->class_name." has no property named '$this', named in the recursion description"); |
849
|
|
|
|
|
|
|
} |
850
|
0
|
|
|
|
|
0
|
my $prior_property_meta = $class_meta->property_meta_for_name($prior); |
851
|
0
|
0
|
|
|
|
0
|
unless ($prior_property_meta) { |
852
|
0
|
|
|
|
|
0
|
Carp::croak("Class ".$class_meta->class_name." has no property named '$prior', named in the recursion description"); |
853
|
|
|
|
|
|
|
} |
854
|
|
|
|
|
|
|
|
855
|
0
|
|
|
|
|
0
|
my $this_class_meta = $this_property_meta->class_meta; |
856
|
0
|
|
|
|
|
0
|
my $prior_class_meta = $prior_property_meta->class_meta; |
857
|
|
|
|
|
|
|
|
858
|
0
|
|
|
|
|
0
|
my $this_table_name = $this_class_meta->table_name; |
859
|
0
|
0
|
|
|
|
0
|
unless ($this_table_name) { |
860
|
0
|
|
|
|
|
0
|
Carp::croak("Cannot resolve table name from class ".$class_meta->class_name." and property '$this', named in the recursion description"); |
861
|
|
|
|
|
|
|
} |
862
|
0
|
|
|
|
|
0
|
my $prior_table_name = $prior_class_meta->table_name; |
863
|
0
|
0
|
|
|
|
0
|
unless ($prior_table_name) { |
864
|
0
|
|
|
|
|
0
|
Carp::croak("Cannot resolve table name from class ".$class_meta->class_name." and property '$prior', named in the recursion description"); |
865
|
|
|
|
|
|
|
} |
866
|
|
|
|
|
|
|
|
867
|
0
|
|
0
|
|
|
0
|
my $this_column_name = $this_property_meta->column_name || $this; |
868
|
0
|
|
0
|
|
|
0
|
my $prior_column_name = $prior_property_meta->column_name || $prior; |
869
|
|
|
|
|
|
|
|
870
|
0
|
|
|
|
|
0
|
$connect_by_clause = "connect by $this_table_name.$this_column_name = prior $prior_table_name.$prior_column_name\n"; |
871
|
|
|
|
|
|
|
} else { |
872
|
5
|
|
|
|
|
11
|
$recurse_resolution_by_iteration = 1; |
873
|
|
|
|
|
|
|
} |
874
|
|
|
|
|
|
|
} |
875
|
|
|
|
|
|
|
|
876
|
655
|
|
|
|
|
857
|
my @property_names_in_resultset_order; |
877
|
655
|
|
|
|
|
1368
|
for my $property_meta_array (@$db_property_data) { |
878
|
3164
|
|
|
|
|
6144
|
push @property_names_in_resultset_order, $property_meta_array->[1]->property_name; |
879
|
|
|
|
|
|
|
} |
880
|
|
|
|
|
|
|
|
881
|
|
|
|
|
|
|
# this is only used when making a real instance object instead of a "set" |
882
|
655
|
|
|
|
|
904
|
my $per_object_in_resultset_loading_detail; |
883
|
655
|
100
|
|
|
|
1620
|
unless ($group_by) { |
884
|
642
|
|
|
|
|
2084
|
$per_object_in_resultset_loading_detail = $ds->_generate_loading_templates_arrayref(\@$db_property_data, $self->_obj_joins); |
885
|
|
|
|
|
|
|
} |
886
|
|
|
|
|
|
|
|
887
|
655
|
100
|
|
|
|
1864
|
if ($group_by) { |
888
|
|
|
|
|
|
|
# when grouping, we're making set objects instead of regular objects |
889
|
|
|
|
|
|
|
# this means that we re-constitute the select clause and add a group_by clause |
890
|
13
|
100
|
|
|
|
36
|
$group_by_clause = 'group by ' . $select_clause if (scalar(@$group_by)); |
891
|
|
|
|
|
|
|
|
892
|
|
|
|
|
|
|
# Q: - does it even make sense for the user to specify an order_by in the |
893
|
|
|
|
|
|
|
# get() request for Set objects? If so, then we need to concatonate these order_by_columns |
894
|
|
|
|
|
|
|
# with the ones that already exist in $order_by_columns from the class data |
895
|
|
|
|
|
|
|
# A: - yes, because group by means "return a list of subsets", and this lets you sort the subsets |
896
|
13
|
|
|
|
|
35
|
$order_by_columns = $ds->_select_clause_columns_for_table_property_data(@$db_property_data); |
897
|
|
|
|
|
|
|
|
898
|
13
|
100
|
|
|
|
35
|
$select_clause .= ', ' if $select_clause; |
899
|
13
|
|
|
|
|
24
|
$select_clause .= 'count(*) count'; |
900
|
13
|
|
|
|
|
24
|
for my $ag (@$aggregate) { |
901
|
10
|
100
|
|
|
|
29
|
next if $ag eq 'count'; |
902
|
|
|
|
|
|
|
# TODO: translate property names to column names, and skip non-column properties |
903
|
4
|
|
|
|
|
7
|
$select_clause .= ', ' . $ag; |
904
|
|
|
|
|
|
|
} |
905
|
13
|
50
|
|
|
|
36
|
unless (@$group_by == @$db_property_data) { |
906
|
0
|
|
|
|
|
0
|
print "mismatch table properties vs group by!\n"; |
907
|
|
|
|
|
|
|
} |
908
|
|
|
|
|
|
|
} |
909
|
|
|
|
|
|
|
|
910
|
655
|
100
|
|
|
|
33391
|
%$self = ( |
911
|
|
|
|
|
|
|
%$self, |
912
|
|
|
|
|
|
|
|
913
|
|
|
|
|
|
|
# custom for RDBMS |
914
|
|
|
|
|
|
|
select_clause => $select_clause, |
915
|
|
|
|
|
|
|
select_hint => scalar(@select_hint) ? \@select_hint : undef, |
916
|
|
|
|
|
|
|
from_clause => $from_clause, |
917
|
|
|
|
|
|
|
connect_by_clause => $connect_by_clause, |
918
|
|
|
|
|
|
|
group_by_clause => $group_by_clause, |
919
|
|
|
|
|
|
|
order_by_columns => $order_by_columns, |
920
|
|
|
|
|
|
|
order_by_non_column_data => $order_by_non_column_data, |
921
|
|
|
|
|
|
|
filter_specs => \@filter_specs, |
922
|
|
|
|
|
|
|
sql_params => \@sql_params, |
923
|
|
|
|
|
|
|
recurse_resolution_by_iteration => $recurse_resolution_by_iteration, |
924
|
|
|
|
|
|
|
|
925
|
|
|
|
|
|
|
# override defaults in the regular datasource $parent_template_data |
926
|
|
|
|
|
|
|
property_names_in_resultset_order => \@property_names_in_resultset_order, |
927
|
|
|
|
|
|
|
properties_meta_in_resultset_order => $db_property_data, # duplicate?! |
928
|
|
|
|
|
|
|
loading_templates => $per_object_in_resultset_loading_detail, |
929
|
|
|
|
|
|
|
); |
930
|
|
|
|
|
|
|
|
931
|
655
|
|
|
|
|
5522
|
my $template_data = $rule_template->{loading_data_cache} = $self; |
932
|
655
|
|
|
|
|
8488
|
return $self; |
933
|
|
|
|
|
|
|
} |
934
|
|
|
|
|
|
|
|
935
|
|
|
|
|
|
|
sub _init_filesystem { |
936
|
33
|
|
|
33
|
|
50
|
my $self = shift; |
937
|
33
|
|
|
|
|
106
|
my $rule_template = $self->rule_template; |
938
|
33
|
|
|
|
|
106
|
my $ds = $self->data_source; |
939
|
|
|
|
|
|
|
|
940
|
|
|
|
|
|
|
# class-based values |
941
|
33
|
|
|
|
|
100
|
my $class_name = $rule_template->subject_class_name; |
942
|
33
|
|
|
|
|
114
|
my $class_meta = $class_name->__meta__; |
943
|
33
|
|
|
|
|
116
|
my $class_data = $ds->_get_class_data_for_loading($class_meta); |
944
|
|
|
|
|
|
|
|
945
|
33
|
|
|
|
|
58
|
my @db_property_data = @{ $class_data->{all_table_properties} }; |
|
33
|
|
|
|
|
94
|
|
946
|
|
|
|
|
|
|
|
947
|
33
|
|
|
|
|
142
|
my($order_by_columns, $order_by_non_column_data) |
948
|
|
|
|
|
|
|
= $self->_determine_complete_order_by_list($rule_template, $class_data, \@db_property_data); |
949
|
|
|
|
|
|
|
|
950
|
33
|
|
|
|
|
803
|
%$self = ( |
951
|
|
|
|
|
|
|
%$self, |
952
|
|
|
|
|
|
|
|
953
|
|
|
|
|
|
|
order_by_columns => $order_by_columns, |
954
|
|
|
|
|
|
|
order_by_non_column_data => $order_by_non_column_data, |
955
|
|
|
|
|
|
|
); |
956
|
|
|
|
|
|
|
|
957
|
33
|
|
|
|
|
211
|
my $template_data = $rule_template->{loading_data_cache} = $self; |
958
|
33
|
|
|
|
|
205
|
return $self; |
959
|
|
|
|
|
|
|
} |
960
|
|
|
|
|
|
|
|
961
|
|
|
|
|
|
|
sub _add_join { |
962
|
148
|
|
|
148
|
|
274
|
my ($self, |
963
|
|
|
|
|
|
|
$property_name, |
964
|
|
|
|
|
|
|
$join, |
965
|
|
|
|
|
|
|
$object_num, |
966
|
|
|
|
|
|
|
$is_optional, |
967
|
|
|
|
|
|
|
$final_accessor, |
968
|
|
|
|
|
|
|
$foreign_data_source, |
969
|
|
|
|
|
|
|
) = @_; |
970
|
|
|
|
|
|
|
|
971
|
148
|
|
33
|
|
|
401
|
my $delegation_chain_data = $self->_delegation_chain_data || $self->_delegation_chain_data({}); |
972
|
148
|
|
50
|
|
|
509
|
my $table_alias = $delegation_chain_data->{"__all__"}{table_alias} ||= {}; |
973
|
148
|
|
100
|
|
|
704
|
my $source_table_and_column_names = $delegation_chain_data->{$property_name}{latest_source_table_and_column_names} ||= []; |
974
|
|
|
|
|
|
|
|
975
|
148
|
|
|
|
|
240
|
my $source_class_name = $join->{source_class}; |
976
|
148
|
|
33
|
|
|
661
|
my $source_class_object = $join->{'source_class_meta'} || $source_class_name->__meta__; |
977
|
|
|
|
|
|
|
|
978
|
148
|
|
50
|
|
|
415
|
my $class_alias = $delegation_chain_data->{"__all__"}{class_alias} ||= {}; |
979
|
148
|
50
|
33
|
|
|
489
|
if (! %$class_alias and $source_class_object->table_name) { |
980
|
0
|
|
|
|
|
0
|
$class_alias->{$source_class_object->table_name} = $source_class_object; |
981
|
|
|
|
|
|
|
} |
982
|
|
|
|
|
|
|
|
983
|
148
|
|
|
|
|
209
|
my $foreign_class_name = $join->{foreign_class}; |
984
|
148
|
|
33
|
|
|
920
|
my $foreign_class_object = $join->{'foreign_class_meta'} || $foreign_class_name->__meta__; |
985
|
|
|
|
|
|
|
|
986
|
148
|
|
|
|
|
507
|
my $rule_template = $self->rule_template; |
987
|
148
|
|
|
|
|
406
|
my $ds = $self->data_source; |
988
|
|
|
|
|
|
|
|
989
|
148
|
|
|
|
|
431
|
my $group_by = $rule_template->group_by; |
990
|
|
|
|
|
|
|
|
991
|
|
|
|
|
|
|
#my($foreign_data_source) = UR::Context->resolve_data_sources_for_class_meta_and_rule($foreign_class_object, $rule_template); |
992
|
148
|
100
|
66
|
|
|
921
|
if (!$foreign_data_source or ($foreign_data_source ne $ds)) { |
993
|
|
|
|
|
|
|
# FIXME - do something smarter in the future where it can do a join-y thing in memory |
994
|
4
|
|
|
|
|
16
|
$self->needs_further_boolexpr_evaluation_after_loading(1); |
995
|
4
|
|
|
|
|
11
|
return; |
996
|
|
|
|
|
|
|
} |
997
|
|
|
|
|
|
|
|
998
|
144
|
|
|
|
|
581
|
my $foreign_class_loading_data = $ds->_get_class_data_for_loading($foreign_class_object); |
999
|
|
|
|
|
|
|
|
1000
|
|
|
|
|
|
|
# This will get filled in during the first pass, and every time after we've successfully |
1001
|
|
|
|
|
|
|
# performed a join - ie. that the delegated property points directly to a class/property |
1002
|
|
|
|
|
|
|
# that is a real table/column, and not a tableless class or another delegated property |
1003
|
144
|
|
|
|
|
221
|
my @source_property_names; |
1004
|
144
|
100
|
|
|
|
419
|
unless (@$source_table_and_column_names) { |
1005
|
138
|
|
|
|
|
170
|
@source_property_names = @{ $join->{source_property_names} }; |
|
138
|
|
|
|
|
450
|
|
1006
|
|
|
|
|
|
|
|
1007
|
|
|
|
|
|
|
@$source_table_and_column_names = |
1008
|
|
|
|
|
|
|
map { |
1009
|
137
|
100
|
|
|
|
856
|
if (my($view, $alias) = $ds->parse_view_and_alias_from_inline_view($_->[0])) { |
1010
|
|
|
|
|
|
|
# This "table_name" was actually a bit of SQL with an inline view and an alias |
1011
|
8
|
|
|
|
|
11
|
$_->[0] = $view; |
1012
|
8
|
|
|
|
|
13
|
$_->[2] = $alias; |
1013
|
|
|
|
|
|
|
} |
1014
|
137
|
|
|
|
|
340
|
$_; |
1015
|
|
|
|
|
|
|
} |
1016
|
|
|
|
|
|
|
map { |
1017
|
138
|
|
|
|
|
273
|
my($p) = $source_class_object->_concrete_property_meta_for_class_and_name($_); |
|
138
|
|
|
|
|
602
|
|
1018
|
138
|
50
|
|
|
|
400
|
unless ($p) { |
1019
|
0
|
|
|
|
|
0
|
Carp::croak("No property $_ for class ".$source_class_object->class_name); |
1020
|
|
|
|
|
|
|
} |
1021
|
138
|
|
|
|
|
603
|
my($table_name,$column_name) = $p->table_and_column_name_for_property(); |
1022
|
138
|
100
|
66
|
|
|
693
|
if ($table_name && $column_name) { |
1023
|
137
|
|
|
|
|
417
|
[$table_name, $column_name]; |
1024
|
|
|
|
|
|
|
} else { |
1025
|
|
|
|
|
|
|
#Carp::confess("Can't determine table and column for property $_ in class " . |
1026
|
|
|
|
|
|
|
# $source_class_object->class_name); |
1027
|
1
|
|
|
|
|
4
|
(); |
1028
|
|
|
|
|
|
|
} |
1029
|
|
|
|
|
|
|
} |
1030
|
|
|
|
|
|
|
@source_property_names; |
1031
|
|
|
|
|
|
|
} |
1032
|
144
|
100
|
|
|
|
380
|
return unless @$source_table_and_column_names; |
1033
|
|
|
|
|
|
|
|
1034
|
|
|
|
|
|
|
#my @source_property_names = @{ $join->{source_property_names} }; |
1035
|
|
|
|
|
|
|
#my ($source_table_name, $fcols, $fprops) = $self->_resolve_table_and_column_data($source_class_object, @source_property_names); |
1036
|
|
|
|
|
|
|
#my @source_column_names = @$fcols; |
1037
|
|
|
|
|
|
|
#my @source_property_meta = @$fprops; |
1038
|
|
|
|
|
|
|
|
1039
|
143
|
|
|
|
|
169
|
my @foreign_property_names = @{ $join->{foreign_property_names} }; |
|
143
|
|
|
|
|
368
|
|
1040
|
143
|
|
|
|
|
547
|
my ($foreign_table_name, $fcols, $fprops) = $self->_resolve_table_and_column_data($foreign_class_object, @foreign_property_names); |
1041
|
143
|
|
|
|
|
266
|
my @foreign_column_names = @$fcols; |
1042
|
143
|
|
|
|
|
217
|
my @foreign_property_meta = @$fprops; |
1043
|
|
|
|
|
|
|
|
1044
|
143
|
50
|
|
|
|
349
|
unless (@foreign_column_names) { |
1045
|
|
|
|
|
|
|
# all calculated properties: don't try to join any further |
1046
|
0
|
|
|
|
|
0
|
return; |
1047
|
|
|
|
|
|
|
} |
1048
|
|
|
|
|
|
|
|
1049
|
143
|
50
|
|
|
|
321
|
unless ($foreign_table_name) { |
1050
|
|
|
|
|
|
|
# If we can't make the join because there is no datasource representation |
1051
|
|
|
|
|
|
|
# for this class, we're done following the joins for this property |
1052
|
|
|
|
|
|
|
# and will NOT try to filter on it at the datasource level |
1053
|
0
|
|
|
|
|
0
|
$self->needs_further_boolexpr_evaluation_after_loading(1); |
1054
|
0
|
|
|
|
|
0
|
return; |
1055
|
|
|
|
|
|
|
} |
1056
|
|
|
|
|
|
|
|
1057
|
143
|
50
|
|
|
|
375
|
unless (@foreign_column_names == @foreign_property_meta) { |
1058
|
|
|
|
|
|
|
# some calculated properties, be sure to re-check for a match after loading the object |
1059
|
0
|
|
|
|
|
0
|
$self->needs_further_boolexpr_evaluation_after_loading(1); |
1060
|
|
|
|
|
|
|
} |
1061
|
|
|
|
|
|
|
|
1062
|
143
|
|
|
|
|
492
|
my $alias = $self->_get_join_alias($join, $property_name); |
1063
|
|
|
|
|
|
|
|
1064
|
143
|
50
|
|
|
|
370
|
unless ($alias) { |
1065
|
143
|
|
|
|
|
459
|
my $alias_num = $self->_alias_count($self->_alias_count+1); |
1066
|
|
|
|
|
|
|
|
1067
|
143
|
|
66
|
|
|
474
|
my $alias_name = $join->sub_group_label || $property_name; |
1068
|
143
|
50
|
|
|
|
462
|
if (substr($alias_name,-1) eq '?') { |
1069
|
0
|
0
|
|
|
|
0
|
chop($alias_name) if substr($alias_name,-1) eq '?'; |
1070
|
|
|
|
|
|
|
} |
1071
|
|
|
|
|
|
|
|
1072
|
143
|
|
|
|
|
283
|
my $alias_length = length($alias_name)+length($alias_num)+1; |
1073
|
143
|
|
|
|
|
160
|
my $alias_max_length = 29; |
1074
|
143
|
50
|
|
|
|
326
|
if ($alias_length > $alias_max_length) { |
1075
|
0
|
|
|
|
|
0
|
$alias = substr($alias_name,0,$alias_max_length-length($alias_num)-1); |
1076
|
|
|
|
|
|
|
} |
1077
|
|
|
|
|
|
|
else { |
1078
|
143
|
|
|
|
|
197
|
$alias = $alias_name; |
1079
|
|
|
|
|
|
|
} |
1080
|
143
|
|
|
|
|
316
|
$alias =~ s/\./_/g; |
1081
|
143
|
|
|
|
|
310
|
$alias .= '_' . $alias_num; |
1082
|
|
|
|
|
|
|
|
1083
|
143
|
|
|
|
|
400
|
$self->_set_join_alias($join, $property_name, $alias); |
1084
|
|
|
|
|
|
|
|
1085
|
143
|
100
|
|
|
|
374
|
if ($foreign_class_object->table_name) { |
1086
|
137
|
|
|
|
|
195
|
my @extra_db_filters; |
1087
|
|
|
|
|
|
|
my @extra_obj_filters; |
1088
|
|
|
|
|
|
|
|
1089
|
|
|
|
|
|
|
# TODO: when "flatten" correctly feeds the "ON" clause we can remove this |
1090
|
|
|
|
|
|
|
# This will crash if the "where" happens to use indirect things |
1091
|
137
|
|
|
|
|
225
|
my $where = $join->{where}; |
1092
|
137
|
100
|
|
|
|
374
|
if ($where) { |
1093
|
39
|
|
|
|
|
159
|
for (my $n = 0; $n < @$where; $n += 2) { |
1094
|
39
|
|
|
|
|
83
|
my $key =$where->[$n]; |
1095
|
39
|
|
|
|
|
184
|
my ($name,$op) = ($key =~ /^(\S+)\s*(.*)/); |
1096
|
|
|
|
|
|
|
|
1097
|
39
|
50
|
|
|
|
137
|
if(index($name, '-') == 0) { |
1098
|
|
|
|
|
|
|
#skip '-order_by', '-hint' and the like for joins |
1099
|
0
|
|
|
|
|
0
|
next; |
1100
|
|
|
|
|
|
|
} |
1101
|
|
|
|
|
|
|
|
1102
|
|
|
|
|
|
|
#my $meta = $foreign_class_object->property_meta_for_name($name); |
1103
|
|
|
|
|
|
|
#my $column = $meta->is_calculated ? (defined($meta->calculate_sql) ? ($meta->calculate_sql) : () ) : ($meta->column_name); |
1104
|
39
|
|
|
|
|
112
|
my ($table_name, $column_names, $property_metas) = $self->_resolve_table_and_column_data($foreign_class_object, $name); |
1105
|
39
|
|
|
|
|
78
|
my $column = $column_names->[0]; |
1106
|
|
|
|
|
|
|
|
1107
|
39
|
50
|
|
|
|
116
|
if (not $column) { |
1108
|
0
|
|
|
|
|
0
|
Carp::confess("No column for $foreign_class_object->{id} $name? Indirect property flattening must be enabled to use indirect filters in where with via/to."); |
1109
|
|
|
|
|
|
|
} |
1110
|
|
|
|
|
|
|
|
1111
|
39
|
|
|
|
|
97
|
my $value = $where->[$n+1]; |
1112
|
39
|
100
|
|
|
|
180
|
push @extra_db_filters, $column => { value => $value, ($op ? (operator => $op) : ()) }; |
1113
|
39
|
100
|
|
|
|
285
|
push @extra_obj_filters, $name => { value => $value, ($op ? (operator => $op) : ()) }; |
1114
|
|
|
|
|
|
|
} |
1115
|
|
|
|
|
|
|
} |
1116
|
|
|
|
|
|
|
|
1117
|
137
|
|
|
|
|
192
|
my @db_join_data; |
1118
|
137
|
|
|
|
|
438
|
for (my $n = 0; $n < @foreign_column_names; $n++) { |
1119
|
|
|
|
|
|
|
|
1120
|
137
|
|
66
|
|
|
735
|
my $link_table_name = $table_alias->{$source_table_and_column_names->[$n][0]} |
1121
|
|
|
|
|
|
|
|| $source_table_and_column_names->[$n][2] |
1122
|
|
|
|
|
|
|
|| $source_table_and_column_names->[$n][0]; |
1123
|
|
|
|
|
|
|
|
1124
|
137
|
|
|
|
|
206
|
my $link_column_name = $source_table_and_column_names->[$n][1]; |
1125
|
|
|
|
|
|
|
|
1126
|
137
|
|
|
|
|
186
|
my $foreign_column_name = $foreign_column_names[$n]; |
1127
|
|
|
|
|
|
|
|
1128
|
137
|
|
66
|
|
|
380
|
my $link_class_meta = $class_alias->{$link_table_name} || $source_class_object; |
1129
|
137
|
|
|
|
|
902
|
my $link_property_name = $link_class_meta->property_for_column($link_column_name); |
1130
|
|
|
|
|
|
|
|
1131
|
|
|
|
|
|
|
# _concrete_property_meta_for_class_and_name returns a list :( |
1132
|
|
|
|
|
|
|
# since we're inspecting the joins by their "real" names and not the generic |
1133
|
|
|
|
|
|
|
# "id", it will only ever return a 1-element list |
1134
|
137
|
|
|
|
|
396
|
my($link_prop) = $link_class_meta->_concrete_property_meta_for_class_and_name($link_property_name); |
1135
|
137
|
|
|
|
|
474
|
my $left_type = $link_prop->_data_type_as_class_name; |
1136
|
137
|
|
|
|
|
387
|
my $right_type = $foreign_property_meta[$n]->_data_type_as_class_name; |
1137
|
137
|
|
|
|
|
516
|
my @coercion = $self->data_source->cast_for_data_conversion($left_type, $right_type, '=', 'join'); |
1138
|
|
|
|
|
|
|
|
1139
|
137
|
|
|
|
|
1073
|
push @db_join_data, |
1140
|
|
|
|
|
|
|
$foreign_column_name => { |
1141
|
|
|
|
|
|
|
link_table_name => $link_table_name, |
1142
|
|
|
|
|
|
|
link_column_name => $link_column_name, |
1143
|
|
|
|
|
|
|
left_coercion => $coercion[0], |
1144
|
|
|
|
|
|
|
right_coercion => $coercion[1], |
1145
|
|
|
|
|
|
|
}; |
1146
|
|
|
|
|
|
|
} |
1147
|
|
|
|
|
|
|
|
1148
|
137
|
|
|
|
|
842
|
$self->_add_db_join( |
1149
|
|
|
|
|
|
|
"$foreign_table_name $alias" => { |
1150
|
|
|
|
|
|
|
@db_join_data, |
1151
|
|
|
|
|
|
|
@extra_db_filters, |
1152
|
|
|
|
|
|
|
} |
1153
|
|
|
|
|
|
|
); |
1154
|
|
|
|
|
|
|
|
1155
|
|
|
|
|
|
|
$self->_add_obj_join( |
1156
|
|
|
|
|
|
|
"$alias" => { |
1157
|
|
|
|
|
|
|
( |
1158
|
|
|
|
|
|
|
map { |
1159
|
137
|
|
|
|
|
471
|
$foreign_property_names[$_] => { |
1160
|
|
|
|
|
|
|
link_class_name => $source_class_name, |
1161
|
137
|
|
66
|
|
|
1592
|
link_alias => $table_alias->{$source_table_and_column_names->[$_][0]} # join alias |
1162
|
|
|
|
|
|
|
|| $source_table_and_column_names->[$_][2] # SQL inline view alias |
1163
|
|
|
|
|
|
|
|| $source_table_and_column_names->[$_][0], # table_name |
1164
|
|
|
|
|
|
|
link_property_name => $source_property_names[$_] |
1165
|
|
|
|
|
|
|
} |
1166
|
|
|
|
|
|
|
} |
1167
|
|
|
|
|
|
|
(0..$#foreign_property_names) |
1168
|
|
|
|
|
|
|
), |
1169
|
|
|
|
|
|
|
@extra_obj_filters, |
1170
|
|
|
|
|
|
|
} |
1171
|
|
|
|
|
|
|
); |
1172
|
|
|
|
|
|
|
|
1173
|
|
|
|
|
|
|
# Add all of the columns in the join table to the return list |
1174
|
|
|
|
|
|
|
# Note that we increment the object numbers. |
1175
|
|
|
|
|
|
|
# Note: we add grouping columns individually instead of in chunks |
1176
|
137
|
100
|
|
|
|
353
|
unless ($group_by) { |
1177
|
|
|
|
|
|
|
$self->_add_columns( |
1178
|
|
|
|
|
|
|
map { |
1179
|
402
|
|
|
|
|
594
|
my $new = [@$_]; |
1180
|
402
|
|
|
|
|
426
|
$new->[2] = $alias; |
1181
|
402
|
|
|
|
|
293
|
$new->[3] = $object_num; |
1182
|
402
|
|
|
|
|
639
|
$new |
1183
|
|
|
|
|
|
|
} |
1184
|
131
|
|
|
|
|
187
|
@{ $foreign_class_loading_data->{direct_table_properties} } |
|
131
|
|
|
|
|
245
|
|
1185
|
|
|
|
|
|
|
); |
1186
|
|
|
|
|
|
|
} |
1187
|
|
|
|
|
|
|
} |
1188
|
|
|
|
|
|
|
|
1189
|
|
|
|
|
|
|
|
1190
|
143
|
100
|
|
|
|
366
|
if ($group_by) { |
1191
|
6
|
100
|
|
|
|
16
|
if ($self->_groups_by_property($property_name)) { |
1192
|
|
|
|
|
|
|
my ($p) = |
1193
|
|
|
|
|
|
|
map { |
1194
|
2
|
|
|
|
|
7
|
my $new = [@$_]; |
1195
|
2
|
|
|
|
|
7
|
$new->[2] = $alias; |
1196
|
2
|
|
|
|
|
2
|
$new->[3] = 0; |
1197
|
2
|
|
|
|
|
4
|
$new |
1198
|
|
|
|
|
|
|
} |
1199
|
8
|
|
|
|
|
14
|
grep { $_->[1]->property_name eq $final_accessor } |
1200
|
2
|
|
|
|
|
5
|
@{ $foreign_class_loading_data->{direct_table_properties} }; |
|
2
|
|
|
|
|
4
|
|
1201
|
2
|
|
|
|
|
8
|
$self->_add_columns($p); |
1202
|
|
|
|
|
|
|
} |
1203
|
|
|
|
|
|
|
} |
1204
|
|
|
|
|
|
|
|
1205
|
|
|
|
|
|
|
|
1206
|
143
|
50
|
|
|
|
428
|
if ($self->_orders_by_property($property_name)) { |
1207
|
|
|
|
|
|
|
my ($p) = |
1208
|
|
|
|
|
|
|
map { |
1209
|
0
|
|
|
|
|
0
|
my $new = [@$_]; |
1210
|
0
|
|
|
|
|
0
|
$new->[2] = $alias; |
1211
|
0
|
|
|
|
|
0
|
$new->[3] = 0; |
1212
|
0
|
|
|
|
|
0
|
$new |
1213
|
|
|
|
|
|
|
} |
1214
|
0
|
|
|
|
|
0
|
grep { $_->[1]->property_name eq $final_accessor } |
1215
|
0
|
|
|
|
|
0
|
@{ $foreign_class_loading_data->{direct_table_properties} }; |
|
0
|
|
|
|
|
0
|
|
1216
|
|
|
|
|
|
|
# ??? what do we do here now with $p? |
1217
|
|
|
|
|
|
|
} |
1218
|
|
|
|
|
|
|
|
1219
|
143
|
100
|
|
|
|
367
|
unless ($is_optional) { |
1220
|
|
|
|
|
|
|
# if _any_ part requires this, mark it required |
1221
|
76
|
|
|
|
|
238
|
$self->_set_alias_required($alias); |
1222
|
|
|
|
|
|
|
} |
1223
|
|
|
|
|
|
|
|
1224
|
|
|
|
|
|
|
} # done adding a new join alias for a join which has not yet been done |
1225
|
|
|
|
|
|
|
|
1226
|
143
|
100
|
|
|
|
390
|
if ($foreign_class_object->table_name) { |
1227
|
137
|
|
|
|
|
317
|
$table_alias->{$foreign_table_name} = $alias; |
1228
|
137
|
|
|
|
|
233
|
$class_alias->{$alias} = $foreign_class_object; |
1229
|
137
|
|
|
|
|
299
|
@$source_table_and_column_names = (); # Flag that we need to re-derive this at the top of the loop |
1230
|
|
|
|
|
|
|
} |
1231
|
|
|
|
|
|
|
|
1232
|
143
|
|
|
|
|
1326
|
return $alias; |
1233
|
|
|
|
|
|
|
} |
1234
|
|
|
|
|
|
|
|
1235
|
|
|
|
|
|
|
sub _resolve_table_and_column_data { |
1236
|
182
|
|
|
182
|
|
303
|
my ($class, $class_meta, @property_names) = @_; |
1237
|
|
|
|
|
|
|
my @property_meta = |
1238
|
182
|
|
|
|
|
267
|
map { $class_meta->_concrete_property_meta_for_class_and_name($_) } |
|
182
|
|
|
|
|
559
|
|
1239
|
|
|
|
|
|
|
@property_names; |
1240
|
182
|
|
|
|
|
244
|
my $table_name; |
1241
|
|
|
|
|
|
|
my @column_names = |
1242
|
|
|
|
|
|
|
map { |
1243
|
|
|
|
|
|
|
# TODO: encapsulate |
1244
|
182
|
50
|
|
|
|
240
|
if ($_->is_calculated) { |
|
182
|
|
|
|
|
517
|
|
1245
|
0
|
0
|
|
|
|
0
|
if ($_->calculate_sql) { |
1246
|
0
|
|
|
|
|
0
|
$_->calculate_sql; |
1247
|
|
|
|
|
|
|
} else { |
1248
|
0
|
|
|
|
|
0
|
(); |
1249
|
|
|
|
|
|
|
} |
1250
|
|
|
|
|
|
|
} else { |
1251
|
182
|
|
|
|
|
195
|
my $column_name; |
1252
|
182
|
|
|
|
|
461
|
($table_name, $column_name) = $_->table_and_column_name_for_property(); |
1253
|
182
|
|
|
|
|
453
|
$column_name; |
1254
|
|
|
|
|
|
|
} |
1255
|
|
|
|
|
|
|
} |
1256
|
|
|
|
|
|
|
@property_meta; |
1257
|
|
|
|
|
|
|
|
1258
|
182
|
100
|
66
|
|
|
1257
|
if ($table_name and $table_name =~ /^(.*)\s+(\w+)\s*$/s) { |
1259
|
8
|
|
|
|
|
14
|
$table_name = $1; |
1260
|
|
|
|
|
|
|
} |
1261
|
|
|
|
|
|
|
|
1262
|
182
|
|
|
|
|
551
|
return ($table_name, \@column_names, \@property_meta); |
1263
|
|
|
|
|
|
|
} |
1264
|
|
|
|
|
|
|
|
1265
|
|
|
|
|
|
|
sub _set_join_alias { |
1266
|
143
|
|
|
143
|
|
240
|
my ($self, $join, $property_name, $alias) = @_; |
1267
|
143
|
|
|
|
|
333
|
$self->_join_data->{$join->id}{$property_name}{alias} = $alias; |
1268
|
143
|
100
|
|
|
|
454
|
$self->_alias_data({}) unless $self->_alias_data(); |
1269
|
143
|
|
|
|
|
324
|
$self->_alias_data->{$alias}{join_id} = $join->id; |
1270
|
|
|
|
|
|
|
} |
1271
|
|
|
|
|
|
|
|
1272
|
|
|
|
|
|
|
sub _get_join_alias { |
1273
|
143
|
|
|
143
|
|
221
|
my ($self,$join,$property_name) = @_; |
1274
|
143
|
100
|
|
|
|
505
|
$self->_join_data({}) unless $self->_join_data(); |
1275
|
143
|
|
|
|
|
333
|
return $self->_join_data->{$join->id}{$property_name}{alias}; |
1276
|
|
|
|
|
|
|
} |
1277
|
|
|
|
|
|
|
|
1278
|
|
|
|
|
|
|
sub _get_alias_join { |
1279
|
2348
|
|
|
2348
|
|
2978
|
my ($self,$alias) = @_; |
1280
|
2348
|
|
|
|
|
6709
|
my $alias_data = $self->_alias_data; |
1281
|
2348
|
100
|
66
|
|
|
9279
|
return if (! $alias_data or ! exists($alias_data->{$alias})); |
1282
|
162
|
|
|
|
|
417
|
my $join_id = $self->_alias_data->{$alias}{join_id}; |
1283
|
162
|
|
|
|
|
763
|
UR::Object::Join->get($join_id); |
1284
|
|
|
|
|
|
|
} |
1285
|
|
|
|
|
|
|
|
1286
|
|
|
|
|
|
|
sub _add_db_join { |
1287
|
137
|
|
|
137
|
|
218
|
my ($self, $key, $data) = @_; |
1288
|
|
|
|
|
|
|
|
1289
|
137
|
|
|
|
|
684
|
my ($alias) = ($key =~/\w+$/); |
1290
|
137
|
|
33
|
|
|
420
|
my $alias_data = $self->_alias_data || $self->_alias_data({}); |
1291
|
137
|
|
|
|
|
353
|
$alias_data->{$alias}{db_join} = $data; |
1292
|
|
|
|
|
|
|
|
1293
|
137
|
|
33
|
|
|
421
|
my $db_joins = $self->_db_joins || $self->_db_joins([]); |
1294
|
137
|
|
|
|
|
366
|
push @$db_joins, $key, $data; |
1295
|
|
|
|
|
|
|
} |
1296
|
|
|
|
|
|
|
|
1297
|
|
|
|
|
|
|
sub _add_obj_join { |
1298
|
137
|
|
|
137
|
|
231
|
my ($self, $key, $data) = @_; |
1299
|
|
|
|
|
|
|
|
1300
|
137
|
50
|
|
|
|
359
|
Carp::confess() unless ref $data; |
1301
|
137
|
|
33
|
|
|
333
|
my $alias_data = $self->_alias_data || $self->_alias_data({}); |
1302
|
137
|
|
|
|
|
296
|
$alias_data->{$key}{obj_join} = $data; # the key is the alias here |
1303
|
|
|
|
|
|
|
|
1304
|
137
|
|
33
|
|
|
384
|
my $obj_joins = $self->_obj_joins || $self->_obj_joins([]); |
1305
|
137
|
|
|
|
|
337
|
push @$obj_joins, $key, $data; |
1306
|
|
|
|
|
|
|
} |
1307
|
|
|
|
|
|
|
|
1308
|
|
|
|
|
|
|
sub _set_alias_required { |
1309
|
76
|
|
|
76
|
|
110
|
my ($self, $alias) = @_; |
1310
|
76
|
|
33
|
|
|
179
|
my $alias_data = $self->_alias_data || $self->_alias_data({}); |
1311
|
76
|
|
|
|
|
150
|
$alias_data->{$alias}{is_required} = 1; |
1312
|
76
|
|
|
|
|
278
|
$alias_data->{$alias}{db_join}{-is_required} = 1; |
1313
|
76
|
|
|
|
|
212
|
$alias_data->{$alias}{obj_join}{-is_required} = 1; |
1314
|
|
|
|
|
|
|
} |
1315
|
|
|
|
|
|
|
|
1316
|
|
|
|
|
|
|
sub _add_columns { |
1317
|
133
|
|
|
133
|
|
178
|
my $self = shift; |
1318
|
133
|
|
|
|
|
267
|
my @new = @_; |
1319
|
133
|
|
|
|
|
396
|
my $old = $self->_db_column_data; |
1320
|
133
|
|
|
|
|
222
|
my $pos = @$old; |
1321
|
133
|
|
|
|
|
208
|
my $lob_column_positions = $self->{lob_column_positions}; |
1322
|
133
|
|
|
|
|
193
|
my $lob_column_names = $self->{lob_column_names}; |
1323
|
133
|
|
|
|
|
234
|
for my $class_property (@new) { |
1324
|
404
|
|
|
|
|
442
|
my ($sql_class,$sql_property,$sql_table_name) = @$class_property; |
1325
|
404
|
|
100
|
|
|
683
|
my $data_type = $sql_property->data_type || ''; |
1326
|
404
|
50
|
|
|
|
717
|
if ($data_type =~ /LOB$/) { |
1327
|
0
|
|
|
|
|
0
|
push @$lob_column_names, $sql_property->column_name; |
1328
|
0
|
|
|
|
|
0
|
push @$lob_column_positions, $pos; |
1329
|
|
|
|
|
|
|
} |
1330
|
404
|
|
|
|
|
449
|
$pos++; |
1331
|
|
|
|
|
|
|
} |
1332
|
133
|
|
|
|
|
437
|
push @$old, @new; |
1333
|
|
|
|
|
|
|
} |
1334
|
|
|
|
|
|
|
|
1335
|
|
|
|
|
|
|
# Used by the object fabricator to find out which resultset column a |
1336
|
|
|
|
|
|
|
# property's data is stored |
1337
|
|
|
|
|
|
|
sub column_index_for_class_property_and_object_num { |
1338
|
152
|
|
|
152
|
0
|
247
|
my($self, $class_name, $property_name, $object_num) = @_; |
1339
|
|
|
|
|
|
|
|
1340
|
152
|
|
50
|
|
|
300
|
$object_num ||= 0; |
1341
|
|
|
|
|
|
|
|
1342
|
152
|
|
|
|
|
458
|
my $db_column_data = $self->_db_column_data; |
1343
|
152
|
|
|
|
|
454
|
for (my $resultset_col = 0; $resultset_col < @$db_column_data; $resultset_col++) { |
1344
|
964
|
100
|
100
|
|
|
1647
|
if ($db_column_data->[$resultset_col]->[1]->class_name eq $class_name |
|
|
|
100
|
|
|
|
|
1345
|
|
|
|
|
|
|
and $db_column_data->[$resultset_col]->[1]->property_name eq $property_name |
1346
|
|
|
|
|
|
|
and $db_column_data->[$resultset_col]->[3] == $object_num |
1347
|
|
|
|
|
|
|
) { |
1348
|
150
|
|
|
|
|
380
|
return $resultset_col; |
1349
|
|
|
|
|
|
|
} |
1350
|
|
|
|
|
|
|
} |
1351
|
2
|
|
|
|
|
5
|
return undef; |
1352
|
|
|
|
|
|
|
} |
1353
|
|
|
|
|
|
|
|
1354
|
|
|
|
|
|
|
# used by the object fabricator to determine the resultset column |
1355
|
|
|
|
|
|
|
# the source property of a join is stored. |
1356
|
|
|
|
|
|
|
sub column_index_for_class_and_property_before_object_num { |
1357
|
93
|
|
|
93
|
0
|
155
|
my($self, $class_name, $property_name, $object_num) = @_; |
1358
|
93
|
50
|
|
|
|
242
|
return unless $object_num; |
1359
|
|
|
|
|
|
|
|
1360
|
93
|
|
|
|
|
239
|
my $db_column_data = $self->_db_column_data; |
1361
|
93
|
|
|
|
|
111
|
my $index; |
1362
|
93
|
|
|
|
|
324
|
for (my $resultset_col = 0; $resultset_col < @$db_column_data; $resultset_col++) { |
1363
|
472
|
100
|
|
|
|
794
|
last if ($db_column_data->[$resultset_col]->[3] >= $object_num); |
1364
|
379
|
100
|
100
|
|
|
591
|
if ($db_column_data->[$resultset_col]->[1]->class_name eq $class_name |
1365
|
|
|
|
|
|
|
and |
1366
|
|
|
|
|
|
|
$db_column_data->[$resultset_col]->[1]->property_name eq $property_name |
1367
|
|
|
|
|
|
|
) { |
1368
|
102
|
|
|
|
|
202
|
$index = $resultset_col; |
1369
|
|
|
|
|
|
|
} |
1370
|
|
|
|
|
|
|
} |
1371
|
93
|
|
|
|
|
192
|
return $index; |
1372
|
|
|
|
|
|
|
} |
1373
|
|
|
|
|
|
|
|
1374
|
|
|
|
|
|
|
|
1375
|
|
|
|
|
|
|
sub _groups_by_property { |
1376
|
6
|
|
|
6
|
|
10
|
my ($self, $property_name) = @_; |
1377
|
6
|
|
|
|
|
16
|
return $self->_group_by_property_names->{$property_name}; |
1378
|
|
|
|
|
|
|
} |
1379
|
|
|
|
|
|
|
|
1380
|
|
|
|
|
|
|
sub _orders_by_property { |
1381
|
143
|
|
|
143
|
|
190
|
my ($self, $property_name) = @_; |
1382
|
143
|
|
|
|
|
393
|
return $self->_order_by_property_names->{$property_name}; |
1383
|
|
|
|
|
|
|
} |
1384
|
|
|
|
|
|
|
|
1385
|
|
|
|
|
|
|
sub _resolve_db_joins_for_inheritance { |
1386
|
655
|
|
|
655
|
|
1045
|
my $class_meta = $_[0]; |
1387
|
|
|
|
|
|
|
|
1388
|
655
|
|
|
|
|
959
|
my $first_table_name; |
1389
|
|
|
|
|
|
|
my @sql_joins; |
1390
|
|
|
|
|
|
|
|
1391
|
0
|
|
|
|
|
0
|
my $prev_table_name; |
1392
|
0
|
|
|
|
|
0
|
my $prev_id_column_name; |
1393
|
0
|
|
|
|
|
0
|
my $prev_property_meta; |
1394
|
|
|
|
|
|
|
|
1395
|
655
|
|
|
|
|
2174
|
my @parent_class_objects = $class_meta->ancestry_class_metas; |
1396
|
|
|
|
|
|
|
|
1397
|
655
|
|
|
|
|
1154
|
my %seen; |
1398
|
655
|
|
|
|
|
1447
|
for my $co ( $class_meta, @parent_class_objects ) { |
1399
|
2222
|
|
|
|
|
5132
|
my $class_name = $co->class_name; |
1400
|
2222
|
100
|
|
|
|
5530
|
next if $seen{$class_name}++; |
1401
|
|
|
|
|
|
|
|
1402
|
2221
|
|
|
|
|
4956
|
my @id_property_objects = $co->direct_id_property_metas; |
1403
|
2221
|
|
|
|
|
3021
|
my %id_properties = map { $_->property_name => 1 } @id_property_objects; |
|
2654
|
|
|
|
|
5245
|
|
1404
|
|
|
|
|
|
|
my @id_column_names = |
1405
|
2221
|
|
|
|
|
2967
|
map { $_->column_name } |
|
2654
|
|
|
|
|
4907
|
|
1406
|
|
|
|
|
|
|
@id_property_objects; |
1407
|
|
|
|
|
|
|
|
1408
|
2221
|
|
|
|
|
5003
|
my $table_name = $co->table_name; |
1409
|
2221
|
100
|
|
|
|
5206
|
if ($table_name) { |
1410
|
703
|
|
66
|
|
|
2917
|
$first_table_name ||= $table_name; |
1411
|
703
|
100
|
|
|
|
1683
|
if ($prev_table_name) { |
1412
|
48
|
50
|
|
|
|
158
|
die "Database-level inheritance cannot be used with multi-value-id classes ($class_name)!" if @id_property_objects > 1; |
1413
|
48
|
|
|
|
|
75
|
my $prev_table_alias; |
1414
|
48
|
100
|
|
|
|
300
|
if ($prev_table_name =~ /.*\s+(\w+)\s*$/) { |
1415
|
2
|
|
|
|
|
9
|
$prev_table_alias = $1; |
1416
|
|
|
|
|
|
|
} |
1417
|
|
|
|
|
|
|
else { |
1418
|
46
|
|
|
|
|
74
|
$prev_table_alias = $prev_table_name; |
1419
|
|
|
|
|
|
|
} |
1420
|
|
|
|
|
|
|
|
1421
|
48
|
|
|
|
|
270
|
my @coercion = $co->data_source->cast_for_data_conversion( |
1422
|
|
|
|
|
|
|
$prev_property_meta->_data_type_as_class_name, |
1423
|
|
|
|
|
|
|
$id_property_objects[0]->_data_type_as_class_name, |
1424
|
|
|
|
|
|
|
'=', |
1425
|
|
|
|
|
|
|
'join'); |
1426
|
48
|
|
|
|
|
202
|
push @sql_joins, |
1427
|
|
|
|
|
|
|
$table_name => |
1428
|
|
|
|
|
|
|
{ |
1429
|
|
|
|
|
|
|
$id_property_objects[0]->column_name => { |
1430
|
|
|
|
|
|
|
link_table_name => $prev_table_alias, |
1431
|
|
|
|
|
|
|
link_column_name => $prev_id_column_name, |
1432
|
|
|
|
|
|
|
left_coercion => $coercion[0], |
1433
|
|
|
|
|
|
|
right_coercion => $coercion[1], |
1434
|
|
|
|
|
|
|
}, |
1435
|
|
|
|
|
|
|
-is_required => 1, |
1436
|
|
|
|
|
|
|
}; |
1437
|
|
|
|
|
|
|
} |
1438
|
703
|
|
|
|
|
1071
|
$prev_table_name = $table_name; |
1439
|
703
|
|
|
|
|
2006
|
$prev_id_column_name = $id_property_objects[0]->column_name; |
1440
|
703
|
|
|
|
|
1812
|
$prev_property_meta = $id_property_objects[0]; |
1441
|
|
|
|
|
|
|
} |
1442
|
|
|
|
|
|
|
} |
1443
|
|
|
|
|
|
|
|
1444
|
655
|
|
|
|
|
2460
|
return ($first_table_name, @sql_joins); |
1445
|
|
|
|
|
|
|
} |
1446
|
|
|
|
|
|
|
|
1447
|
|
|
|
|
|
|
sub _resolve_object_join_data_for_property_chain { |
1448
|
94
|
|
|
94
|
|
176
|
my ($rule_template, $property_name) = @_; |
1449
|
94
|
|
|
|
|
298
|
my $class_meta = $rule_template->subject_class_name->__meta__; |
1450
|
|
|
|
|
|
|
|
1451
|
94
|
|
|
|
|
153
|
my @joins; |
1452
|
|
|
|
|
|
|
my $is_optional; |
1453
|
0
|
|
|
|
|
0
|
my $final_accessor; |
1454
|
|
|
|
|
|
|
|
1455
|
94
|
|
|
|
|
505
|
my @pmeta = $class_meta->_concrete_property_meta_for_class_and_name($property_name); |
1456
|
|
|
|
|
|
|
|
1457
|
94
|
|
|
|
|
165
|
my $last_class_meta = $class_meta; |
1458
|
94
|
|
|
|
|
211
|
for my $meta (@pmeta) { |
1459
|
114
|
50
|
|
|
|
291
|
if (!$meta) { |
1460
|
0
|
|
|
|
|
0
|
Carp::croak "Can't resolve joins for ".$rule_template->subject_class_name . " property '$property_name': No property metadata found for that class and property_name"; |
1461
|
|
|
|
|
|
|
} |
1462
|
|
|
|
|
|
|
#id is a special property that we want to look up, but isn't necessarily on a table |
1463
|
|
|
|
|
|
|
#so if it aliases another property, we look at that instead |
1464
|
114
|
100
|
100
|
|
|
318
|
if($meta->property_name eq 'id' and $meta->class_name eq 'UR::Object') { |
1465
|
2
|
|
|
|
|
93
|
my @id_properties = grep {$_->class_name ne 'UR::Object'} $last_class_meta->id_properties; |
|
4
|
|
|
|
|
8
|
|
1466
|
2
|
50
|
|
|
|
7
|
if(@id_properties == 1) { |
|
|
0
|
|
|
|
|
|
1467
|
2
|
|
|
|
|
4
|
$meta = $id_properties[0]; |
1468
|
2
|
|
|
|
|
7
|
$last_class_meta = $meta->class_name->__meta__; |
1469
|
2
|
|
|
|
|
5
|
next; |
1470
|
|
|
|
|
|
|
} |
1471
|
|
|
|
|
|
|
elsif (@id_properties > 1) { |
1472
|
0
|
|
|
|
|
0
|
Carp::confess "can't join to class " . $last_class_meta->class_name . " with multiple id properties: @id_properties"; |
1473
|
|
|
|
|
|
|
} |
1474
|
|
|
|
|
|
|
} |
1475
|
112
|
100
|
100
|
|
|
334
|
if($meta->data_type and $meta->data_type =~ /::/) { |
1476
|
25
|
|
|
|
|
84
|
$last_class_meta = UR::Object::Type->get($meta->data_type); |
1477
|
|
|
|
|
|
|
} else { |
1478
|
87
|
|
|
|
|
288
|
$last_class_meta = UR::Object::Type->get($meta->class_name); |
1479
|
|
|
|
|
|
|
} |
1480
|
112
|
50
|
|
|
|
418
|
last unless $last_class_meta; |
1481
|
|
|
|
|
|
|
} |
1482
|
|
|
|
|
|
|
|
1483
|
|
|
|
|
|
|
# we can't actually get this from the joins because |
1484
|
|
|
|
|
|
|
# a bunch of optional things can be chained together to form |
1485
|
|
|
|
|
|
|
# something non-optional |
1486
|
94
|
|
|
|
|
188
|
$is_optional = 0; |
1487
|
94
|
|
|
|
|
196
|
for my $pmeta (@pmeta) { |
1488
|
114
|
|
|
|
|
470
|
push @joins, $pmeta->_resolve_join_chain(); |
1489
|
114
|
100
|
100
|
|
|
413
|
$is_optional = 1 if $pmeta->is_optional or $pmeta->is_many; |
1490
|
|
|
|
|
|
|
} |
1491
|
|
|
|
|
|
|
|
1492
|
94
|
50
|
|
|
|
301
|
return unless @joins; |
1493
|
94
|
|
|
|
|
420
|
return ($joins[-1]->{source_name_for_foreign}, $is_optional, @joins) |
1494
|
|
|
|
|
|
|
}; |
1495
|
|
|
|
|
|
|
|
1496
|
|
|
|
|
|
|
sub _init_light { |
1497
|
655
|
|
|
655
|
|
989
|
my $self = shift; |
1498
|
655
|
|
|
|
|
2487
|
my $rule_template = $self->rule_template; |
1499
|
655
|
|
|
|
|
1738
|
my $ds = $self->data_source; |
1500
|
|
|
|
|
|
|
|
1501
|
655
|
|
|
|
|
1968
|
my $class_name = $rule_template->subject_class_name; |
1502
|
655
|
|
|
|
|
3077
|
my $class_meta = $class_name->__meta__; |
1503
|
655
|
|
|
|
|
4013
|
my $class_data = $ds->_get_class_data_for_loading($class_meta); |
1504
|
|
|
|
|
|
|
|
1505
|
655
|
|
|
|
|
926
|
my @parent_class_objects = @{ $class_data->{parent_class_objects} }; |
|
655
|
|
|
|
|
1951
|
|
1506
|
655
|
|
|
|
|
948
|
my @all_properties = @{ $class_data->{all_properties} }; |
|
655
|
|
|
|
|
1871
|
|
1507
|
655
|
|
|
|
|
1125
|
my $sub_classification_meta_class_name = $class_data->{sub_classification_meta_class_name}; |
1508
|
655
|
|
|
|
|
1046
|
my $subclassify_by = $class_data->{subclassify_by}; |
1509
|
|
|
|
|
|
|
|
1510
|
655
|
|
|
|
|
843
|
my @all_id_property_names = @{ $class_data->{all_id_property_names} }; |
|
655
|
|
|
|
|
1630
|
|
1511
|
655
|
|
|
|
|
915
|
my @id_properties = @{ $class_data->{id_properties} }; |
|
655
|
|
|
|
|
1515
|
|
1512
|
655
|
|
|
|
|
1097
|
my $id_property_sorter = $class_data->{id_property_sorter}; |
1513
|
655
|
|
|
|
|
1054
|
my $sub_typing_property = $class_data->{sub_typing_property}; |
1514
|
655
|
|
|
|
|
977
|
my $class_table_name = $class_data->{class_table_name}; |
1515
|
|
|
|
|
|
|
|
1516
|
655
|
|
|
|
|
2002
|
my $recursion_desc = $rule_template->recursion_desc; |
1517
|
655
|
|
|
|
|
957
|
my $recurse_property_on_this_row; |
1518
|
|
|
|
|
|
|
my $recurse_property_referencing_other_rows; |
1519
|
0
|
|
|
|
|
0
|
my $recurse_resolution_by_iteration; |
1520
|
655
|
100
|
|
|
|
1677
|
if ($recursion_desc) { |
1521
|
5
|
|
|
|
|
10
|
($recurse_property_on_this_row,$recurse_property_referencing_other_rows) = @$recursion_desc; |
1522
|
5
|
|
|
|
|
28
|
$recurse_resolution_by_iteration = ! $ds->does_support_recursive_queries; |
1523
|
|
|
|
|
|
|
} |
1524
|
|
|
|
|
|
|
|
1525
|
655
|
|
|
|
|
869
|
my $needs_further_boolexpr_evaluation_after_loading; |
1526
|
|
|
|
|
|
|
|
1527
|
|
|
|
|
|
|
my $is_join_across_data_source; |
1528
|
|
|
|
|
|
|
|
1529
|
0
|
|
|
|
|
0
|
my @sql_params; |
1530
|
0
|
|
|
|
|
0
|
my @filter_specs; |
1531
|
0
|
|
|
|
|
0
|
my @property_names_in_resultset_order; |
1532
|
655
|
|
|
|
|
910
|
my $object_num = 0; # 0-based, usually zero unless there are joins |
1533
|
|
|
|
|
|
|
|
1534
|
655
|
|
|
|
|
2456
|
my @filters = $rule_template->_property_names; |
1535
|
|
|
|
|
|
|
my %filters = |
1536
|
1059
|
|
|
|
|
2601
|
map { $_ => 0 } |
1537
|
655
|
|
|
|
|
1478
|
grep { substr($_,0,1) ne '-' } |
|
1059
|
|
|
|
|
2624
|
|
1538
|
|
|
|
|
|
|
@filters; |
1539
|
|
|
|
|
|
|
|
1540
|
655
|
100
|
66
|
|
|
2758
|
unless (@all_id_property_names == 1 && $all_id_property_names[0] eq "id") { |
1541
|
630
|
|
|
|
|
1012
|
delete $filters{'id'}; |
1542
|
|
|
|
|
|
|
} |
1543
|
|
|
|
|
|
|
|
1544
|
|
|
|
|
|
|
my ( |
1545
|
655
|
|
|
|
|
985
|
@sql_joins, |
1546
|
|
|
|
|
|
|
@sql_filters, |
1547
|
|
|
|
|
|
|
$prev_table_name, |
1548
|
|
|
|
|
|
|
$prev_id_column_name, |
1549
|
|
|
|
|
|
|
$pk_used, |
1550
|
|
|
|
|
|
|
@delegated_properties, |
1551
|
|
|
|
|
|
|
%chain_delegates, |
1552
|
|
|
|
|
|
|
); |
1553
|
|
|
|
|
|
|
|
1554
|
655
|
|
|
|
|
1730
|
for my $key (keys %filters) { |
1555
|
911
|
100
|
|
|
|
2832
|
if (index($key,'.') != -1) { |
1556
|
18
|
|
|
|
|
62
|
$chain_delegates{$key} = delete $filters{$key}; |
1557
|
|
|
|
|
|
|
} |
1558
|
|
|
|
|
|
|
} |
1559
|
|
|
|
|
|
|
|
1560
|
655
|
|
|
|
|
1368
|
for my $co ( $class_meta, @parent_class_objects ) { |
1561
|
2221
|
|
|
|
|
5303
|
my $class_name = $co->class_name; |
1562
|
2221
|
100
|
66
|
|
|
13264
|
last if ( ($class_name eq 'UR::Object') or (not $class_name->isa("UR::Object")) ); |
1563
|
1566
|
|
|
|
|
5712
|
my @id_property_objects = $co->direct_id_property_metas; |
1564
|
1566
|
50
|
|
|
|
3643
|
if (@id_property_objects == 0) { |
1565
|
0
|
|
|
|
|
0
|
@id_property_objects = $co->property_meta_for_name("id"); |
1566
|
0
|
0
|
|
|
|
0
|
if (@id_property_objects == 0) { |
1567
|
0
|
|
|
|
|
0
|
Carp::confess("Couldn't determine ID properties for $class_name\n"); |
1568
|
|
|
|
|
|
|
} |
1569
|
|
|
|
|
|
|
} |
1570
|
1566
|
|
|
|
|
2355
|
my %id_properties = map { $_->property_name => 1 } @id_property_objects; |
|
1999
|
|
|
|
|
4111
|
|
1571
|
|
|
|
|
|
|
my @id_column_names = |
1572
|
1566
|
|
|
|
|
2198
|
map { $_->column_name } |
|
1999
|
|
|
|
|
3882
|
|
1573
|
|
|
|
|
|
|
@id_property_objects; |
1574
|
1566
|
|
|
|
|
3813
|
for my $property_name (sort keys %filters) { |
1575
|
934
|
|
|
|
|
3433
|
my $property = UR::Object::Property->get(class_name => $class_name, property_name => $property_name); |
1576
|
934
|
100
|
|
|
|
2515
|
next unless $property; |
1577
|
893
|
|
|
|
|
3182
|
my $operator = $rule_template->operator_for($property_name); |
1578
|
893
|
|
|
|
|
3223
|
my $value_position = $rule_template->value_position_for_property_name($property_name); |
1579
|
893
|
|
|
|
|
1568
|
delete $filters{$property_name}; |
1580
|
893
|
100
|
|
|
|
2284
|
$pk_used = 1 if $id_properties{ $property_name }; |
1581
|
893
|
50
|
100
|
|
|
2825
|
if ($property->is_legacy_eav) { |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
1582
|
0
|
|
|
|
|
0
|
die "Old GSC EAV can be handled with a via/to/where/is_mutable=1"; |
1583
|
|
|
|
|
|
|
} |
1584
|
|
|
|
|
|
|
elsif ($property->is_delegated) { |
1585
|
51
|
|
|
|
|
203
|
push @delegated_properties, $property; |
1586
|
|
|
|
|
|
|
} |
1587
|
|
|
|
|
|
|
elsif ($property->is_calculated || $property->is_transient) { |
1588
|
7
|
|
|
|
|
19
|
$needs_further_boolexpr_evaluation_after_loading = 1; |
1589
|
|
|
|
|
|
|
} |
1590
|
|
|
|
|
|
|
else { |
1591
|
835
|
|
|
|
|
4288
|
push @sql_filters, |
1592
|
|
|
|
|
|
|
$class_name => |
1593
|
|
|
|
|
|
|
{ |
1594
|
|
|
|
|
|
|
$property_name => { operator => $operator, value_position => $value_position } |
1595
|
|
|
|
|
|
|
}; |
1596
|
|
|
|
|
|
|
} |
1597
|
|
|
|
|
|
|
} |
1598
|
1566
|
|
|
|
|
3633
|
$prev_id_column_name = $id_property_objects[0]->column_name; |
1599
|
|
|
|
|
|
|
} # end of inheritance loop |
1600
|
|
|
|
|
|
|
|
1601
|
655
|
50
|
|
|
|
2315
|
if ( my @errors = keys(%filters) ) { |
1602
|
0
|
|
|
|
|
0
|
my $class_name = $class_meta->class_name; |
1603
|
0
|
|
|
|
|
0
|
$ds->error_message('Unknown param(s) (' . join(',',@errors) . ") used to generate SQL for $class_name!"); |
1604
|
0
|
|
|
|
|
0
|
Carp::confess(); |
1605
|
|
|
|
|
|
|
} |
1606
|
|
|
|
|
|
|
|
1607
|
655
|
|
|
|
|
1174
|
my $last_class_name = $class_name; |
1608
|
655
|
|
|
|
|
929
|
my $last_class_object = $class_meta; |
1609
|
655
|
|
|
|
|
951
|
my $alias_num = 1; |
1610
|
655
|
|
|
|
|
924
|
my %joins_done; |
1611
|
|
|
|
|
|
|
my $joins_across_data_sources; |
1612
|
|
|
|
|
|
|
|
1613
|
|
|
|
|
|
|
DELEGATED_PROPERTY: |
1614
|
655
|
|
|
|
|
1436
|
for my $delegated_property (@delegated_properties) { |
1615
|
51
|
|
|
|
|
79
|
my $last_alias_for_this_chain; |
1616
|
51
|
|
|
|
|
158
|
my $property_name = $delegated_property->property_name; |
1617
|
51
|
|
|
|
|
302
|
my @joins = $delegated_property->_resolve_join_chain($property_name); |
1618
|
51
|
|
|
|
|
234
|
my $relationship_name = $delegated_property->via; |
1619
|
51
|
50
|
|
|
|
165
|
unless ($relationship_name) { |
1620
|
0
|
|
|
|
|
0
|
$relationship_name = $property_name; |
1621
|
0
|
|
|
|
|
0
|
$needs_further_boolexpr_evaluation_after_loading = 1; |
1622
|
|
|
|
|
|
|
} |
1623
|
|
|
|
|
|
|
|
1624
|
51
|
|
|
|
|
245
|
my $delegate_class_meta = $delegated_property->class_meta; |
1625
|
51
|
|
|
|
|
383
|
my($via_accessor_meta) = $delegate_class_meta->_concrete_property_meta_for_class_and_name($relationship_name); |
1626
|
51
|
50
|
|
|
|
168
|
next unless $via_accessor_meta; |
1627
|
51
|
|
|
|
|
189
|
my $final_accessor = $delegated_property->to; |
1628
|
|
|
|
|
|
|
|
1629
|
51
|
|
|
|
|
173
|
my $data_type = $via_accessor_meta->data_type; |
1630
|
51
|
50
|
|
|
|
167
|
unless ($data_type) { |
1631
|
0
|
|
|
|
|
0
|
Carp::croak "Can't resolve delegation for $property_name on class $class_name: via property $relationship_name has no data type"; |
1632
|
|
|
|
|
|
|
} |
1633
|
|
|
|
|
|
|
|
1634
|
51
|
|
|
|
|
162
|
my $data_type_meta = UR::Object::Type->get($via_accessor_meta->data_type); |
1635
|
51
|
50
|
|
|
|
192
|
unless ($data_type_meta) { |
1636
|
0
|
|
|
|
|
0
|
Carp::croak "No class meta data for " . $via_accessor_meta->data_type . |
1637
|
|
|
|
|
|
|
" while resolving property $property_name on class $class_name"; |
1638
|
|
|
|
|
|
|
} |
1639
|
51
|
|
|
|
|
360
|
my($final_accessor_meta) = $data_type_meta->_concrete_property_meta_for_class_and_name( |
1640
|
|
|
|
|
|
|
$final_accessor |
1641
|
|
|
|
|
|
|
); |
1642
|
51
|
50
|
|
|
|
166
|
unless ($final_accessor_meta) { |
1643
|
0
|
|
|
|
|
0
|
Carp::croak("No property '$final_accessor' on class " . $via_accessor_meta->data_type . |
1644
|
|
|
|
|
|
|
" while resolving property $property_name on class $class_name"); |
1645
|
|
|
|
|
|
|
} |
1646
|
|
|
|
|
|
|
|
1647
|
|
|
|
|
|
|
# Follow the chain of via/to delegation down to where the data ultimately lives |
1648
|
51
|
|
|
|
|
201
|
while($final_accessor_meta->is_delegated) { |
1649
|
|
|
|
|
|
|
# May have been 'to' an id_by/id_class_by property. Stop chaining and do two queries |
1650
|
|
|
|
|
|
|
# If we had access to the value at this point, we could continue joining through that |
1651
|
|
|
|
|
|
|
# value's class and id |
1652
|
10
|
50
|
33
|
|
|
38
|
next DELEGATED_PROPERTY if ($final_accessor_meta->id_by or $final_accessor_meta->id_class_by); |
1653
|
|
|
|
|
|
|
|
1654
|
10
|
|
|
|
|
18
|
my $prev_accessor_meta = $final_accessor_meta; |
1655
|
10
|
|
|
|
|
48
|
$final_accessor_meta = $final_accessor_meta->to_property_meta(); |
1656
|
10
|
50
|
|
|
|
46
|
unless ($final_accessor_meta) { |
1657
|
0
|
|
|
|
|
0
|
Carp::croak("Can't resolve property '$final_accessor' of class " . $via_accessor_meta->data_type |
1658
|
|
|
|
|
|
|
. ": Resolution involved property '" . $prev_accessor_meta->property_name . "' of class " |
1659
|
|
|
|
|
|
|
. $prev_accessor_meta->class_name |
1660
|
|
|
|
|
|
|
. " which is delegated, but its via/to metadata does not resolve to a known class and property"); |
1661
|
|
|
|
|
|
|
} |
1662
|
|
|
|
|
|
|
} |
1663
|
51
|
|
|
|
|
169
|
$final_accessor = $final_accessor_meta->property_name; |
1664
|
51
|
|
|
|
|
126
|
for my $join (@joins) { |
1665
|
114
|
|
|
|
|
232
|
my $source_class_name = $join->{source_class}; |
1666
|
114
|
|
33
|
|
|
575
|
my $source_class_object = $join->{'source_class_meta'} || $source_class_name->__meta__; |
1667
|
|
|
|
|
|
|
|
1668
|
114
|
|
|
|
|
174
|
my $foreign_class_name = $join->{foreign_class}; |
1669
|
114
|
100
|
|
|
|
765
|
next DELEGATED_PROPERTY if ($foreign_class_name->isa('UR::Value')); |
1670
|
66
|
|
33
|
|
|
353
|
my $foreign_class_object = $join->{'foreign_class_meta'} || $foreign_class_name->__meta__; |
1671
|
66
|
|
|
|
|
228
|
my($foreign_data_source) = $UR::Context::current->resolve_data_sources_for_class_meta_and_rule($foreign_class_object, $rule_template); |
1672
|
66
|
50
|
66
|
|
|
923
|
if (! $foreign_data_source) { |
|
|
100
|
66
|
|
|
|
|
1673
|
0
|
|
|
|
|
0
|
$needs_further_boolexpr_evaluation_after_loading = 1; |
1674
|
0
|
|
|
|
|
0
|
next DELEGATED_PROPERTY; |
1675
|
|
|
|
|
|
|
|
1676
|
|
|
|
|
|
|
} elsif ($foreign_data_source ne $ds or |
1677
|
|
|
|
|
|
|
! $ds->does_support_joins or |
1678
|
|
|
|
|
|
|
! $foreign_data_source->does_support_joins |
1679
|
|
|
|
|
|
|
) |
1680
|
|
|
|
|
|
|
{ |
1681
|
3
|
|
|
|
|
4
|
push(@{$joins_across_data_sources->{$foreign_data_source->id}}, $delegated_property); |
|
3
|
|
|
|
|
14
|
|
1682
|
3
|
|
|
|
|
13
|
next DELEGATED_PROPERTY; |
1683
|
|
|
|
|
|
|
} |
1684
|
63
|
|
|
|
|
103
|
my @source_property_names = @{ $join->{source_property_names} }; |
|
63
|
|
|
|
|
213
|
|
1685
|
|
|
|
|
|
|
my @source_table_and_column_names = |
1686
|
|
|
|
|
|
|
map { |
1687
|
63
|
|
|
|
|
132
|
my($p) = $source_class_object->_concrete_property_meta_for_class_and_name($_); |
|
63
|
|
|
|
|
197
|
|
1688
|
63
|
50
|
|
|
|
169
|
unless ($p) { |
1689
|
0
|
|
|
|
|
0
|
Carp::confess("No property $_ for class $source_class_object->{class_name}\n"); |
1690
|
|
|
|
|
|
|
} |
1691
|
63
|
50
|
|
|
|
252
|
unless ($p->class_name->__meta__) { |
1692
|
0
|
|
|
|
|
0
|
Carp::croak("Can't get class metadata for " . $p->class_name); |
1693
|
|
|
|
|
|
|
} |
1694
|
63
|
|
|
|
|
194
|
[$p->class_name->__meta__->class_name, $p->property_name]; |
1695
|
|
|
|
|
|
|
} |
1696
|
|
|
|
|
|
|
@source_property_names; |
1697
|
63
|
|
|
|
|
99
|
my $foreign_table_name = $foreign_class_name; |
1698
|
63
|
50
|
|
|
|
162
|
unless ($foreign_table_name) { |
1699
|
|
|
|
|
|
|
# If we can't make the join because there is no datasource representation |
1700
|
|
|
|
|
|
|
# for this class, we're done following the joins for this property |
1701
|
|
|
|
|
|
|
# and will NOT try to filter on it at the datasource level |
1702
|
0
|
|
|
|
|
0
|
$needs_further_boolexpr_evaluation_after_loading = 1; |
1703
|
0
|
|
|
|
|
0
|
next DELEGATED_PROPERTY; |
1704
|
|
|
|
|
|
|
} |
1705
|
63
|
|
|
|
|
90
|
my @foreign_property_names = @{ $join->{foreign_property_names} }; |
|
63
|
|
|
|
|
159
|
|
1706
|
|
|
|
|
|
|
my @foreign_property_meta = |
1707
|
|
|
|
|
|
|
map { |
1708
|
63
|
|
|
|
|
110
|
$foreign_class_object->_concrete_property_meta_for_class_and_name($_) |
|
63
|
|
|
|
|
252
|
|
1709
|
|
|
|
|
|
|
} |
1710
|
|
|
|
|
|
|
@foreign_property_names; |
1711
|
|
|
|
|
|
|
|
1712
|
|
|
|
|
|
|
my @foreign_column_names = |
1713
|
|
|
|
|
|
|
map { |
1714
|
|
|
|
|
|
|
# TODO: encapsulate |
1715
|
63
|
0
|
|
|
|
102
|
$_->is_calculated ? (defined($_->calculate_sql) ? ($_->calculate_sql) : () ) : ($_->property_name) |
|
63
|
50
|
|
|
|
239
|
|
1716
|
|
|
|
|
|
|
} |
1717
|
|
|
|
|
|
|
@foreign_property_meta; |
1718
|
|
|
|
|
|
|
|
1719
|
63
|
50
|
|
|
|
166
|
unless (@foreign_column_names) { |
1720
|
|
|
|
|
|
|
# all calculated properties: don't try to join any further |
1721
|
0
|
|
|
|
|
0
|
last; |
1722
|
|
|
|
|
|
|
} |
1723
|
63
|
50
|
|
|
|
183
|
unless (@foreign_column_names == @foreign_property_meta) { |
1724
|
|
|
|
|
|
|
# some calculated properties, be sure to re-check for a match after loading the object |
1725
|
0
|
|
|
|
|
0
|
$needs_further_boolexpr_evaluation_after_loading = 1; |
1726
|
|
|
|
|
|
|
} |
1727
|
63
|
|
|
|
|
146
|
my $alias = $joins_done{$join->{id}}; |
1728
|
63
|
100
|
|
|
|
171
|
unless ($alias) { |
1729
|
60
|
|
|
|
|
173
|
$alias = "${relationship_name}_${alias_num}"; |
1730
|
60
|
|
|
|
|
121
|
$alias_num++; |
1731
|
60
|
|
|
|
|
79
|
$object_num++; |
1732
|
|
|
|
|
|
|
|
1733
|
|
|
|
|
|
|
push @sql_joins, |
1734
|
|
|
|
|
|
|
"$foreign_table_name $alias" => |
1735
|
|
|
|
|
|
|
{ |
1736
|
|
|
|
|
|
|
map { |
1737
|
60
|
|
66
|
|
|
220
|
$foreign_property_names[$_] => { |
|
60
|
|
|
|
|
471
|
|
1738
|
|
|
|
|
|
|
link_table_name => $last_alias_for_this_chain || $source_table_and_column_names[$_][0], |
1739
|
|
|
|
|
|
|
link_column_name => $source_table_and_column_names[$_][1] |
1740
|
|
|
|
|
|
|
} |
1741
|
|
|
|
|
|
|
} |
1742
|
|
|
|
|
|
|
(0..$#foreign_property_names) |
1743
|
|
|
|
|
|
|
}; |
1744
|
|
|
|
|
|
|
|
1745
|
|
|
|
|
|
|
# Add all of the columns in the join table to the return list. |
1746
|
|
|
|
|
|
|
push @all_properties, |
1747
|
173
|
|
|
|
|
407
|
map { [$foreign_class_object, $_, $alias, $object_num] } |
1748
|
173
|
|
|
|
|
242
|
map { $_->[1] } # These three lines are to get around a bug in perl |
1749
|
152
|
|
|
|
|
271
|
sort { $a->[0] cmp $b->[0] } # 5.8's sort involving method calls within the sort |
1750
|
173
|
|
|
|
|
301
|
map { [ $_->property_name, $_ ] } # sub that do sorts of their own |
1751
|
60
|
100
|
|
|
|
292
|
grep { defined($_->column_name) && $_->column_name ne '' } |
|
281
|
|
|
|
|
506
|
|
1752
|
|
|
|
|
|
|
UR::Object::Property->get( class_name => $foreign_class_name ); |
1753
|
|
|
|
|
|
|
|
1754
|
60
|
|
|
|
|
257
|
$joins_done{$join->{id}} = $alias; |
1755
|
|
|
|
|
|
|
|
1756
|
|
|
|
|
|
|
} |
1757
|
|
|
|
|
|
|
# Set these for after all of the joins are done |
1758
|
63
|
|
|
|
|
106
|
$last_class_name = $foreign_class_name; |
1759
|
63
|
|
|
|
|
94
|
$last_class_object = $foreign_class_object; |
1760
|
63
|
|
|
|
|
209
|
$last_alias_for_this_chain = $alias; |
1761
|
|
|
|
|
|
|
} # next join |
1762
|
0
|
0
|
|
|
|
0
|
unless ($delegated_property->via) { |
1763
|
0
|
|
|
|
|
0
|
next; |
1764
|
|
|
|
|
|
|
} |
1765
|
0
|
|
|
|
|
0
|
my($final_accessor_property_meta) = $last_class_object->_concrete_property_meta_for_class_and_name($final_accessor); |
1766
|
0
|
0
|
|
|
|
0
|
unless ($final_accessor_property_meta) { |
1767
|
0
|
|
|
|
|
0
|
Carp::croak("No property metadata for property named '$final_accessor' in class " . $last_class_object->class_name |
1768
|
|
|
|
|
|
|
. " while resolving joins for property '" .$delegated_property->property_name . "' in class " |
1769
|
|
|
|
|
|
|
. $delegated_property->class_name); |
1770
|
|
|
|
|
|
|
} |
1771
|
0
|
|
|
|
|
0
|
my $sql_lvalue; |
1772
|
0
|
0
|
|
|
|
0
|
if ($final_accessor_property_meta->is_calculated) { |
1773
|
0
|
|
|
|
|
0
|
$sql_lvalue = $final_accessor_property_meta->calculate_sql; |
1774
|
0
|
0
|
|
|
|
0
|
unless (defined($sql_lvalue)) { |
1775
|
0
|
|
|
|
|
0
|
$needs_further_boolexpr_evaluation_after_loading = 1; |
1776
|
0
|
|
|
|
|
0
|
next; |
1777
|
|
|
|
|
|
|
} |
1778
|
|
|
|
|
|
|
} |
1779
|
|
|
|
|
|
|
else { |
1780
|
0
|
|
|
|
|
0
|
$sql_lvalue = $final_accessor_property_meta->column_name; |
1781
|
0
|
0
|
|
|
|
0
|
unless (defined($sql_lvalue)) { |
1782
|
0
|
|
|
|
|
0
|
Carp::confess("No column name set for non-delegated/calculated property $property_name of $class_name"); |
1783
|
|
|
|
|
|
|
} |
1784
|
|
|
|
|
|
|
} |
1785
|
0
|
|
|
|
|
0
|
my $operator = $rule_template->operator_for($property_name); |
1786
|
0
|
|
|
|
|
0
|
my $value_position = $rule_template->value_position_for_property_name($property_name); |
1787
|
|
|
|
|
|
|
} # next delegated property |
1788
|
655
|
|
|
|
|
1265
|
for my $property_meta_array (@all_properties) { |
1789
|
5071
|
|
|
|
|
8218
|
push @property_names_in_resultset_order, $property_meta_array->[1]->property_name; |
1790
|
|
|
|
|
|
|
} |
1791
|
655
|
100
|
|
|
|
1790
|
my $rule_template_without_recursion_desc = ($recursion_desc ? $rule_template->remove_filter('-recurse') : $rule_template); |
1792
|
655
|
|
|
|
|
879
|
my $rule_template_specifies_value_for_subtype; |
1793
|
655
|
100
|
|
|
|
1713
|
if ($sub_typing_property) { |
1794
|
61
|
|
|
|
|
282
|
$rule_template_specifies_value_for_subtype = $rule_template->specifies_value_for($sub_typing_property) |
1795
|
|
|
|
|
|
|
} |
1796
|
|
|
|
|
|
|
#my $per_object_in_resultset_loading_detail = $ds->_generate_loading_templates_arrayref(\@all_properties); |
1797
|
655
|
|
100
|
|
|
6996
|
%$self = ( |
1798
|
|
|
|
|
|
|
%$self, |
1799
|
|
|
|
|
|
|
%$class_data, |
1800
|
|
|
|
|
|
|
properties_for_params => \@all_properties, |
1801
|
|
|
|
|
|
|
property_names_in_resultset_order => \@property_names_in_resultset_order, |
1802
|
|
|
|
|
|
|
joins => \@sql_joins, |
1803
|
|
|
|
|
|
|
rule_template_id => $rule_template->id, |
1804
|
|
|
|
|
|
|
rule_template_without_recursion_desc => $rule_template_without_recursion_desc, |
1805
|
|
|
|
|
|
|
rule_template_id_without_recursion_desc => $rule_template_without_recursion_desc->id, |
1806
|
|
|
|
|
|
|
rule_matches_all => $rule_template->matches_all, |
1807
|
|
|
|
|
|
|
rule_specifies_id => ($rule_template->specifies_value_for('id') || undef), |
1808
|
|
|
|
|
|
|
rule_template_is_id_only => $rule_template->is_id_only, |
1809
|
|
|
|
|
|
|
rule_template_specifies_value_for_subtype => $rule_template_specifies_value_for_subtype, |
1810
|
|
|
|
|
|
|
recursion_desc => $rule_template->recursion_desc, |
1811
|
|
|
|
|
|
|
recurse_property_on_this_row => $recurse_property_on_this_row, |
1812
|
|
|
|
|
|
|
recurse_property_referencing_other_rows => $recurse_property_referencing_other_rows, |
1813
|
|
|
|
|
|
|
recurse_resolution_by_iteration => $recurse_resolution_by_iteration, |
1814
|
|
|
|
|
|
|
#loading_templates => $per_object_in_resultset_loading_detail, |
1815
|
|
|
|
|
|
|
joins_across_data_sources => $joins_across_data_sources, |
1816
|
|
|
|
|
|
|
); |
1817
|
655
|
|
|
|
|
7067
|
return $self; |
1818
|
|
|
|
|
|
|
} |
1819
|
|
|
|
|
|
|
|
1820
|
|
|
|
|
|
|
sub _init_core { |
1821
|
143
|
|
|
143
|
|
239
|
my $self = shift; |
1822
|
143
|
|
|
|
|
550
|
my $rule_template = $self->rule_template; |
1823
|
143
|
|
|
|
|
418
|
my $ds = $self->data_source; |
1824
|
|
|
|
|
|
|
|
1825
|
|
|
|
|
|
|
# TODO: most of this only applies to the RDBMS subclass, |
1826
|
|
|
|
|
|
|
# but some applies to any datasource. It doesn't hurt to have the RDBMS stuff |
1827
|
|
|
|
|
|
|
# here and ignored, but it's not placed correctly. |
1828
|
|
|
|
|
|
|
|
1829
|
|
|
|
|
|
|
# class-based values |
1830
|
|
|
|
|
|
|
|
1831
|
143
|
|
|
|
|
446
|
my $class_name = $rule_template->subject_class_name; |
1832
|
143
|
|
|
|
|
660
|
my $class_meta = $class_name->__meta__; |
1833
|
143
|
|
|
|
|
743
|
my $class_data = $ds->_get_class_data_for_loading($class_meta); |
1834
|
|
|
|
|
|
|
|
1835
|
143
|
|
|
|
|
207
|
my @parent_class_objects = @{ $class_data->{parent_class_objects} }; |
|
143
|
|
|
|
|
400
|
|
1836
|
143
|
|
|
|
|
190
|
my @all_properties = @{ $class_data->{all_properties} }; |
|
143
|
|
|
|
|
339
|
|
1837
|
143
|
|
|
|
|
220
|
my $sub_classification_meta_class_name = $class_data->{sub_classification_meta_class_name}; |
1838
|
143
|
|
|
|
|
205
|
my $subclassify_by = $class_data->{subclassify_by}; |
1839
|
|
|
|
|
|
|
|
1840
|
143
|
|
|
|
|
202
|
my @all_id_property_names = @{ $class_data->{all_id_property_names} }; |
|
143
|
|
|
|
|
300
|
|
1841
|
143
|
|
|
|
|
189
|
my @id_properties = @{ $class_data->{id_properties} }; |
|
143
|
|
|
|
|
277
|
|
1842
|
143
|
|
|
|
|
226
|
my $id_property_sorter = $class_data->{id_property_sorter}; |
1843
|
|
|
|
|
|
|
|
1844
|
143
|
|
|
|
|
196
|
my $sub_typing_property = $class_data->{sub_typing_property}; |
1845
|
143
|
|
|
|
|
184
|
my $class_table_name = $class_data->{class_table_name}; |
1846
|
|
|
|
|
|
|
|
1847
|
|
|
|
|
|
|
# individual query/boolexpr based |
1848
|
|
|
|
|
|
|
|
1849
|
143
|
|
|
|
|
422
|
my $recursion_desc = $rule_template->recursion_desc; |
1850
|
143
|
|
|
|
|
197
|
my $recurse_property_on_this_row; |
1851
|
|
|
|
|
|
|
my $recurse_property_referencing_other_rows; |
1852
|
143
|
50
|
|
|
|
360
|
if ($recursion_desc) { |
1853
|
0
|
|
|
|
|
0
|
($recurse_property_on_this_row,$recurse_property_referencing_other_rows) = @$recursion_desc; |
1854
|
|
|
|
|
|
|
} |
1855
|
|
|
|
|
|
|
|
1856
|
|
|
|
|
|
|
# _usually_ items freshly loaded from the DB don't need to be evaluated through the rule |
1857
|
|
|
|
|
|
|
# because the SQL gets constructed in such a way that all the items returned would pass anyway. |
1858
|
|
|
|
|
|
|
# But in certain cases (a delegated property trying to match a non-object value (which is a bug |
1859
|
|
|
|
|
|
|
# in the caller's code from one point of view) or with calculated non-sql properties, then the |
1860
|
|
|
|
|
|
|
# sql will return a superset of the items we're actually asking for, and the loader needs to |
1861
|
|
|
|
|
|
|
# validate them through the rule |
1862
|
143
|
|
|
|
|
176
|
my $needs_further_boolexpr_evaluation_after_loading; |
1863
|
|
|
|
|
|
|
|
1864
|
|
|
|
|
|
|
# Does fulfilling this request involve querying more than one data source? |
1865
|
|
|
|
|
|
|
my $is_join_across_data_source; |
1866
|
|
|
|
|
|
|
|
1867
|
0
|
|
|
|
|
0
|
my @sql_params; |
1868
|
0
|
|
|
|
|
0
|
my @filter_specs; |
1869
|
0
|
|
|
|
|
0
|
my @property_names_in_resultset_order; |
1870
|
143
|
|
|
|
|
191
|
my $object_num = 0; # 0-based, usually zero unless there are joins |
1871
|
|
|
|
|
|
|
|
1872
|
143
|
|
|
|
|
576
|
my @filters = $rule_template->_property_names; |
1873
|
|
|
|
|
|
|
my %filters = |
1874
|
189
|
|
|
|
|
458
|
map { $_ => 0 } |
1875
|
143
|
|
|
|
|
328
|
grep { substr($_,0,1) ne '-' } |
|
189
|
|
|
|
|
468
|
|
1876
|
|
|
|
|
|
|
@filters; |
1877
|
|
|
|
|
|
|
|
1878
|
143
|
100
|
66
|
|
|
693
|
unless (@all_id_property_names == 1 && $all_id_property_names[0] eq "id") { |
1879
|
99
|
|
|
|
|
166
|
delete $filters{'id'}; |
1880
|
|
|
|
|
|
|
} |
1881
|
|
|
|
|
|
|
|
1882
|
|
|
|
|
|
|
my ( |
1883
|
143
|
|
|
|
|
194
|
@sql_joins, |
1884
|
|
|
|
|
|
|
@sql_filters, |
1885
|
|
|
|
|
|
|
$prev_table_name, |
1886
|
|
|
|
|
|
|
$prev_id_column_name, |
1887
|
|
|
|
|
|
|
$pk_used, |
1888
|
|
|
|
|
|
|
@delegated_properties, |
1889
|
|
|
|
|
|
|
%chain_delegates, |
1890
|
|
|
|
|
|
|
); |
1891
|
|
|
|
|
|
|
|
1892
|
143
|
|
|
|
|
406
|
for my $key (keys %filters) { |
1893
|
154
|
100
|
|
|
|
560
|
if (index($key,'.') != -1) { |
1894
|
2
|
|
|
|
|
7
|
$chain_delegates{$key} = delete $filters{$key}; |
1895
|
|
|
|
|
|
|
} |
1896
|
|
|
|
|
|
|
} |
1897
|
143
|
|
|
|
|
282
|
for my $co ( $class_meta, @parent_class_objects ) { |
1898
|
398
|
|
|
|
|
940
|
my $class_name = $co->class_name; |
1899
|
|
|
|
|
|
|
|
1900
|
398
|
100
|
66
|
|
|
2188
|
last if ( ($class_name eq 'UR::Object') or (not $class_name->isa("UR::Object")) ); |
1901
|
|
|
|
|
|
|
|
1902
|
255
|
|
|
|
|
1141
|
my @id_property_objects = $co->direct_id_property_metas; |
1903
|
|
|
|
|
|
|
|
1904
|
255
|
50
|
|
|
|
600
|
if (@id_property_objects == 0) { |
1905
|
0
|
|
|
|
|
0
|
@id_property_objects = $co->property_meta_for_name("id"); |
1906
|
0
|
0
|
|
|
|
0
|
if (@id_property_objects == 0) { |
1907
|
0
|
|
|
|
|
0
|
Carp::confess("Couldn't determine ID properties for $class_name\n"); |
1908
|
|
|
|
|
|
|
} |
1909
|
|
|
|
|
|
|
} |
1910
|
|
|
|
|
|
|
|
1911
|
255
|
|
|
|
|
420
|
my %id_properties = map { $_->property_name => 1 } @id_property_objects; |
|
270
|
|
|
|
|
688
|
|
1912
|
|
|
|
|
|
|
my @id_column_names = |
1913
|
255
|
|
|
|
|
395
|
map { $_->column_name } |
|
270
|
|
|
|
|
642
|
|
1914
|
|
|
|
|
|
|
@id_property_objects; |
1915
|
|
|
|
|
|
|
|
1916
|
255
|
|
|
|
|
695
|
for my $property_name (sort keys %filters) |
1917
|
|
|
|
|
|
|
{ |
1918
|
191
|
|
|
|
|
640
|
my $property = UR::Object::Property->get(class_name => $class_name, property_name => $property_name); |
1919
|
191
|
100
|
|
|
|
510
|
next unless $property; |
1920
|
|
|
|
|
|
|
|
1921
|
152
|
|
|
|
|
516
|
my $operator = $rule_template->operator_for($property_name); |
1922
|
152
|
|
|
|
|
542
|
my $value_position = $rule_template->value_position_for_property_name($property_name); |
1923
|
|
|
|
|
|
|
|
1924
|
152
|
|
|
|
|
246
|
delete $filters{$property_name}; |
1925
|
152
|
100
|
|
|
|
420
|
$pk_used = 1 if $id_properties{ $property_name }; |
1926
|
|
|
|
|
|
|
|
1927
|
152
|
50
|
|
|
|
504
|
if ($property->is_legacy_eav) { |
|
|
50
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
1928
|
0
|
|
|
|
|
0
|
die "Old GSC EAV can be handled with a via/to/where/is_mutable=1"; |
1929
|
|
|
|
|
|
|
} |
1930
|
|
|
|
|
|
|
elsif ($property->is_transient) { |
1931
|
0
|
|
|
|
|
0
|
die "Query by transient property $property_name on $class_name cannot be done!"; |
1932
|
|
|
|
|
|
|
} |
1933
|
|
|
|
|
|
|
elsif ($property->is_delegated) { |
1934
|
7
|
|
|
|
|
19
|
push @delegated_properties, $property; |
1935
|
|
|
|
|
|
|
} |
1936
|
|
|
|
|
|
|
elsif ($property->is_calculated) { |
1937
|
0
|
|
|
|
|
0
|
$needs_further_boolexpr_evaluation_after_loading = 1; |
1938
|
|
|
|
|
|
|
} |
1939
|
|
|
|
|
|
|
else { |
1940
|
|
|
|
|
|
|
# normal column: filter on it |
1941
|
145
|
|
|
|
|
728
|
push @sql_filters, |
1942
|
|
|
|
|
|
|
$class_name => |
1943
|
|
|
|
|
|
|
{ |
1944
|
|
|
|
|
|
|
$property_name => { operator => $operator, value_position => $value_position } |
1945
|
|
|
|
|
|
|
}; |
1946
|
|
|
|
|
|
|
} |
1947
|
|
|
|
|
|
|
} |
1948
|
|
|
|
|
|
|
|
1949
|
255
|
|
|
|
|
673
|
$prev_id_column_name = $id_property_objects[0]->column_name; |
1950
|
|
|
|
|
|
|
|
1951
|
|
|
|
|
|
|
} # end of inheritance loop |
1952
|
|
|
|
|
|
|
|
1953
|
143
|
50
|
|
|
|
528
|
if ( my @errors = keys(%filters) ) { |
1954
|
0
|
|
|
|
|
0
|
my $class_name = $class_meta->class_name; |
1955
|
0
|
|
|
|
|
0
|
$ds->error_message('Unknown param(s) (' . join(',',@errors) . ") used to generate SQL for $class_name!"); |
1956
|
0
|
|
|
|
|
0
|
Carp::confess(); |
1957
|
|
|
|
|
|
|
} |
1958
|
|
|
|
|
|
|
|
1959
|
143
|
|
|
|
|
241
|
my $last_class_name = $class_name; |
1960
|
143
|
|
|
|
|
188
|
my $last_class_object = $class_meta; |
1961
|
143
|
|
|
|
|
180
|
my $alias_num = 1; |
1962
|
|
|
|
|
|
|
|
1963
|
143
|
|
|
|
|
215
|
my %joins_done; |
1964
|
|
|
|
|
|
|
my $joins_across_data_sources; |
1965
|
|
|
|
|
|
|
|
1966
|
|
|
|
|
|
|
DELEGATED_PROPERTY: |
1967
|
143
|
|
|
|
|
277
|
for my $delegated_property (@delegated_properties) { |
1968
|
7
|
|
|
|
|
13
|
my $last_alias_for_this_chain; |
1969
|
|
|
|
|
|
|
|
1970
|
7
|
|
|
|
|
20
|
my $property_name = $delegated_property->property_name; |
1971
|
7
|
|
|
|
|
32
|
my @joins = $delegated_property->_resolve_join_chain($property_name); |
1972
|
|
|
|
|
|
|
#pop @joins if $joins[-1]->{foreign_class}->isa("UR::Value"); |
1973
|
7
|
|
|
|
|
28
|
my $relationship_name = $delegated_property->via; |
1974
|
7
|
50
|
|
|
|
21
|
unless ($relationship_name) { |
1975
|
0
|
|
|
|
|
0
|
$relationship_name = $property_name; |
1976
|
0
|
|
|
|
|
0
|
$needs_further_boolexpr_evaluation_after_loading = 1; |
1977
|
|
|
|
|
|
|
} |
1978
|
|
|
|
|
|
|
|
1979
|
7
|
|
|
|
|
30
|
my $delegate_class_meta = $delegated_property->class_meta; |
1980
|
7
|
|
|
|
|
39
|
my($via_accessor_meta) = $delegate_class_meta->_concrete_property_meta_for_class_and_name($relationship_name); |
1981
|
7
|
|
|
|
|
19
|
my $final_accessor = $delegated_property->to; |
1982
|
7
|
|
|
|
|
20
|
my($final_accessor_meta) = $via_accessor_meta->data_type->__meta__->_concrete_property_meta_for_class_and_name( |
1983
|
|
|
|
|
|
|
$final_accessor |
1984
|
|
|
|
|
|
|
); |
1985
|
7
|
50
|
|
|
|
21
|
unless ($final_accessor_meta) { |
1986
|
0
|
|
|
|
|
0
|
Carp::croak("No property '$final_accessor' on class " . $via_accessor_meta->data_type . |
1987
|
|
|
|
|
|
|
" while resolving property $property_name on class $class_name"); |
1988
|
|
|
|
|
|
|
} |
1989
|
7
|
|
|
|
|
21
|
while($final_accessor_meta->is_delegated) { |
1990
|
0
|
|
|
|
|
0
|
$final_accessor_meta = $final_accessor_meta->to_property_meta(); |
1991
|
0
|
0
|
|
|
|
0
|
unless ($final_accessor_meta) { |
1992
|
0
|
|
|
|
|
0
|
Carp::croak("No property '$final_accessor' on class " . $via_accessor_meta->data_type . |
1993
|
|
|
|
|
|
|
" while resolving property $property_name on class $class_name"); |
1994
|
|
|
|
|
|
|
} |
1995
|
|
|
|
|
|
|
} |
1996
|
7
|
|
|
|
|
19
|
$final_accessor = $final_accessor_meta->property_name; |
1997
|
|
|
|
|
|
|
|
1998
|
7
|
|
|
|
|
16
|
for my $join (@joins) { |
1999
|
|
|
|
|
|
|
|
2000
|
7
|
|
|
|
|
16
|
my $source_class_name = $join->{source_class}; |
2001
|
7
|
|
33
|
|
|
39
|
my $source_class_object = $join->{'source_class_meta'} || $source_class_name->__meta__; |
2002
|
|
|
|
|
|
|
|
2003
|
7
|
|
|
|
|
12
|
my $foreign_class_name = $join->{foreign_class}; |
2004
|
7
|
|
33
|
|
|
39
|
my $foreign_class_object = $join->{'foreign_class_meta'} || $foreign_class_name->__meta__; |
2005
|
7
|
|
|
|
|
27
|
my($foreign_data_source) = $UR::Context::current->resolve_data_sources_for_class_meta_and_rule($foreign_class_object, $rule_template); |
2006
|
7
|
50
|
66
|
|
|
71
|
if (! $foreign_data_source) { |
|
|
50
|
33
|
|
|
|
|
2007
|
0
|
|
|
|
|
0
|
$needs_further_boolexpr_evaluation_after_loading = 1; |
2008
|
0
|
|
|
|
|
0
|
next DELEGATED_PROPERTY; |
2009
|
|
|
|
|
|
|
|
2010
|
|
|
|
|
|
|
} elsif ($foreign_data_source ne $ds or |
2011
|
|
|
|
|
|
|
! $ds->does_support_joins or |
2012
|
|
|
|
|
|
|
! $foreign_data_source->does_support_joins |
2013
|
|
|
|
|
|
|
) |
2014
|
|
|
|
|
|
|
{ |
2015
|
7
|
|
|
|
|
9
|
push(@{$joins_across_data_sources->{$foreign_data_source->id}}, $delegated_property); |
|
7
|
|
|
|
|
25
|
|
2016
|
7
|
|
|
|
|
27
|
next DELEGATED_PROPERTY; |
2017
|
|
|
|
|
|
|
} |
2018
|
|
|
|
|
|
|
|
2019
|
0
|
|
|
|
|
0
|
my @source_property_names = @{ $join->{source_property_names} }; |
|
0
|
|
|
|
|
0
|
|
2020
|
|
|
|
|
|
|
|
2021
|
|
|
|
|
|
|
my @source_table_and_column_names = |
2022
|
|
|
|
|
|
|
map { |
2023
|
0
|
|
|
|
|
0
|
my($p) = $source_class_object->_concrete_property_meta_for_class_and_name($_); |
|
0
|
|
|
|
|
0
|
|
2024
|
0
|
0
|
|
|
|
0
|
unless ($p) { |
2025
|
0
|
|
|
|
|
0
|
Carp::confess("No property $_ for class $source_class_object->{class_name}\n"); |
2026
|
|
|
|
|
|
|
} |
2027
|
0
|
|
|
|
|
0
|
[$p->class_name->__meta__->class_name, $p->property_name]; |
2028
|
|
|
|
|
|
|
} |
2029
|
|
|
|
|
|
|
@source_property_names; |
2030
|
|
|
|
|
|
|
|
2031
|
|
|
|
|
|
|
|
2032
|
0
|
|
|
|
|
0
|
my $foreign_table_name = $foreign_class_name; |
2033
|
|
|
|
|
|
|
|
2034
|
0
|
0
|
|
|
|
0
|
unless ($foreign_table_name) { |
2035
|
|
|
|
|
|
|
# If we can't make the join because there is no datasource representation |
2036
|
|
|
|
|
|
|
# for this class, we're done following the joins for this property |
2037
|
|
|
|
|
|
|
# and will NOT try to filter on it at the datasource level |
2038
|
0
|
|
|
|
|
0
|
$needs_further_boolexpr_evaluation_after_loading = 1; |
2039
|
0
|
|
|
|
|
0
|
next DELEGATED_PROPERTY; |
2040
|
|
|
|
|
|
|
} |
2041
|
|
|
|
|
|
|
|
2042
|
0
|
|
|
|
|
0
|
my @foreign_property_names = @{ $join->{foreign_property_names} }; |
|
0
|
|
|
|
|
0
|
|
2043
|
|
|
|
|
|
|
my @foreign_property_meta = |
2044
|
|
|
|
|
|
|
map { |
2045
|
0
|
|
|
|
|
0
|
$foreign_class_object->_concrete_property_meta_for_class_and_name($_); |
|
0
|
|
|
|
|
0
|
|
2046
|
|
|
|
|
|
|
} |
2047
|
|
|
|
|
|
|
@foreign_property_names; |
2048
|
|
|
|
|
|
|
|
2049
|
|
|
|
|
|
|
my @foreign_column_names = |
2050
|
|
|
|
|
|
|
map { |
2051
|
|
|
|
|
|
|
# TODO: encapsulate |
2052
|
0
|
0
|
|
|
|
0
|
$_->is_calculated ? (defined($_->calculate_sql) ? ($_->calculate_sql) : () ) : ($_->property_name) |
|
0
|
0
|
|
|
|
0
|
|
2053
|
|
|
|
|
|
|
} |
2054
|
|
|
|
|
|
|
@foreign_property_meta; |
2055
|
|
|
|
|
|
|
|
2056
|
0
|
0
|
|
|
|
0
|
unless (@foreign_column_names) { |
2057
|
|
|
|
|
|
|
# all calculated properties: don't try to join any further |
2058
|
0
|
|
|
|
|
0
|
last; |
2059
|
|
|
|
|
|
|
} |
2060
|
0
|
0
|
|
|
|
0
|
unless (@foreign_column_names == @foreign_property_meta) { |
2061
|
|
|
|
|
|
|
# some calculated properties, be sure to re-check for a match after loading the object |
2062
|
0
|
|
|
|
|
0
|
$needs_further_boolexpr_evaluation_after_loading = 1; |
2063
|
|
|
|
|
|
|
} |
2064
|
|
|
|
|
|
|
|
2065
|
0
|
|
|
|
|
0
|
my $alias = $joins_done{$join->{id}}; |
2066
|
0
|
0
|
|
|
|
0
|
unless ($alias) { |
2067
|
0
|
|
|
|
|
0
|
$alias = "${relationship_name}_${alias_num}"; |
2068
|
0
|
|
|
|
|
0
|
$alias_num++; |
2069
|
0
|
|
|
|
|
0
|
$object_num++; |
2070
|
|
|
|
|
|
|
|
2071
|
0
|
|
|
|
|
0
|
my @source_property_meta = map { $source_class_object->_concrete_property_meta_for_class_and_name($_) } |
|
0
|
|
|
|
|
0
|
|
2072
|
|
|
|
|
|
|
@source_property_names; |
2073
|
|
|
|
|
|
|
push @sql_joins, |
2074
|
|
|
|
|
|
|
"$foreign_table_name $alias" => |
2075
|
|
|
|
|
|
|
{ |
2076
|
|
|
|
|
|
|
map { |
2077
|
0
|
|
|
|
|
0
|
my @coercion = $ds->cast_for_data_conversion( |
|
0
|
|
|
|
|
0
|
|
2078
|
|
|
|
|
|
|
$source_property_meta[$_]->_data_type_as_class_name, |
2079
|
|
|
|
|
|
|
$foreign_property_meta[$_]->_data_type_as_class_name, |
2080
|
|
|
|
|
|
|
'=', |
2081
|
|
|
|
|
|
|
'join'); |
2082
|
0
|
|
0
|
|
|
0
|
$foreign_property_names[$_] => { |
2083
|
|
|
|
|
|
|
link_table_name => $last_alias_for_this_chain || $source_table_and_column_names[$_][0], |
2084
|
|
|
|
|
|
|
link_column_name => $source_table_and_column_names[$_][1], |
2085
|
|
|
|
|
|
|
left_coercion => $coercion[0], |
2086
|
|
|
|
|
|
|
right_coercion => $coercion[1], |
2087
|
|
|
|
|
|
|
} |
2088
|
|
|
|
|
|
|
} |
2089
|
|
|
|
|
|
|
(0..$#foreign_property_names) |
2090
|
|
|
|
|
|
|
}; |
2091
|
|
|
|
|
|
|
|
2092
|
|
|
|
|
|
|
# Add all of the columns in the join table to the return list. |
2093
|
|
|
|
|
|
|
push @all_properties, |
2094
|
0
|
|
|
|
|
0
|
map { [$foreign_class_object, $_, $alias, $object_num] } |
2095
|
0
|
|
|
|
|
0
|
map { $_->[1] } # These three lines are to get around a bug in perl |
2096
|
0
|
|
|
|
|
0
|
sort { $a->[0] cmp $b->[0] } # 5.8's sort involving method calls within the sort |
2097
|
0
|
|
|
|
|
0
|
map { [ $_->property_name, $_ ] } # sub that do sorts of their own |
2098
|
0
|
0
|
|
|
|
0
|
grep { defined($_->column_name) && $_->column_name ne '' } |
|
0
|
|
|
|
|
0
|
|
2099
|
|
|
|
|
|
|
UR::Object::Property->get( class_name => $foreign_class_name ); |
2100
|
|
|
|
|
|
|
|
2101
|
0
|
|
|
|
|
0
|
$joins_done{$join->{id}} = $alias; |
2102
|
|
|
|
|
|
|
|
2103
|
|
|
|
|
|
|
} |
2104
|
|
|
|
|
|
|
|
2105
|
|
|
|
|
|
|
# Set these for after all of the joins are done |
2106
|
0
|
|
|
|
|
0
|
$last_class_name = $foreign_class_name; |
2107
|
0
|
|
|
|
|
0
|
$last_class_object = $foreign_class_object; |
2108
|
0
|
|
|
|
|
0
|
$last_alias_for_this_chain = $alias; |
2109
|
|
|
|
|
|
|
|
2110
|
|
|
|
|
|
|
} # next join |
2111
|
|
|
|
|
|
|
|
2112
|
0
|
0
|
|
|
|
0
|
unless ($delegated_property->via) { |
2113
|
0
|
|
|
|
|
0
|
next; |
2114
|
|
|
|
|
|
|
} |
2115
|
|
|
|
|
|
|
|
2116
|
0
|
|
|
|
|
0
|
my($final_accessor_property_meta) = $last_class_object->_concrete_property_meta_for_class_and_name($id_properties[0]); |
2117
|
0
|
0
|
|
|
|
0
|
unless ($final_accessor_property_meta) { |
2118
|
0
|
|
|
|
|
0
|
Carp::croak("No property metadata for property named '$final_accessor' in class " . $last_class_object->class_name |
2119
|
|
|
|
|
|
|
. " while resolving joins for property '" .$delegated_property->property_name . "' in class " |
2120
|
|
|
|
|
|
|
. $delegated_property->class_name); |
2121
|
|
|
|
|
|
|
} |
2122
|
|
|
|
|
|
|
|
2123
|
0
|
|
|
|
|
0
|
my $sql_lvalue; |
2124
|
0
|
0
|
|
|
|
0
|
if ($final_accessor_property_meta->is_calculated) { |
2125
|
0
|
|
|
|
|
0
|
$sql_lvalue = $final_accessor_property_meta->calculate_sql; |
2126
|
0
|
0
|
|
|
|
0
|
unless (defined($sql_lvalue)) { |
2127
|
0
|
|
|
|
|
0
|
$needs_further_boolexpr_evaluation_after_loading = 1; |
2128
|
0
|
|
|
|
|
0
|
next; |
2129
|
|
|
|
|
|
|
} |
2130
|
|
|
|
|
|
|
} |
2131
|
|
|
|
|
|
|
else { |
2132
|
0
|
|
|
|
|
0
|
$sql_lvalue = $final_accessor_property_meta->column_name; |
2133
|
0
|
0
|
|
|
|
0
|
unless (defined($sql_lvalue)) { |
2134
|
0
|
|
|
|
|
0
|
Carp::confess("No column name set for non-delegated/calculated property $property_name of $class_name"); |
2135
|
|
|
|
|
|
|
} |
2136
|
|
|
|
|
|
|
} |
2137
|
|
|
|
|
|
|
|
2138
|
0
|
|
|
|
|
0
|
my $operator = $rule_template->operator_for($property_name); |
2139
|
0
|
|
|
|
|
0
|
my $value_position = $rule_template->value_position_for_property_name($property_name); |
2140
|
|
|
|
|
|
|
} # next delegated property |
2141
|
|
|
|
|
|
|
|
2142
|
143
|
|
|
|
|
263
|
for my $property_meta_array (@all_properties) { |
2143
|
636
|
|
|
|
|
1119
|
push @property_names_in_resultset_order, $property_meta_array->[1]->property_name; |
2144
|
|
|
|
|
|
|
} |
2145
|
|
|
|
|
|
|
|
2146
|
143
|
50
|
|
|
|
369
|
my $rule_template_without_recursion_desc = ($recursion_desc ? $rule_template->remove_filter('-recurse') : $rule_template); |
2147
|
|
|
|
|
|
|
|
2148
|
143
|
|
|
|
|
208
|
my $rule_template_specifies_value_for_subtype; |
2149
|
143
|
100
|
|
|
|
362
|
if ($sub_typing_property) { |
2150
|
1
|
|
|
|
|
6
|
$rule_template_specifies_value_for_subtype = $rule_template->specifies_value_for($sub_typing_property) |
2151
|
|
|
|
|
|
|
} |
2152
|
|
|
|
|
|
|
|
2153
|
143
|
100
|
66
|
|
|
264
|
my @this_ds_properties = grep { ! $_->[1]->is_delegated |
|
636
|
|
|
|
|
981
|
|
2154
|
|
|
|
|
|
|
and (! $_->[1]->is_calculated or $_->[1]->calculate_sql) |
2155
|
|
|
|
|
|
|
} |
2156
|
|
|
|
|
|
|
@all_properties; |
2157
|
|
|
|
|
|
|
|
2158
|
143
|
|
|
|
|
863
|
my $per_object_in_resultset_loading_detail = $ds->_generate_loading_templates_arrayref(\@this_ds_properties); |
2159
|
|
|
|
|
|
|
|
2160
|
143
|
|
100
|
|
|
1291
|
%$self = ( |
2161
|
|
|
|
|
|
|
%$self, |
2162
|
|
|
|
|
|
|
|
2163
|
|
|
|
|
|
|
%$class_data, |
2164
|
|
|
|
|
|
|
|
2165
|
|
|
|
|
|
|
properties_for_params => \@all_properties, |
2166
|
|
|
|
|
|
|
property_names_in_resultset_order => \@property_names_in_resultset_order, |
2167
|
|
|
|
|
|
|
joins => \@sql_joins, |
2168
|
|
|
|
|
|
|
|
2169
|
|
|
|
|
|
|
rule_template_id => $rule_template->id, |
2170
|
|
|
|
|
|
|
rule_template_without_recursion_desc => $rule_template_without_recursion_desc, |
2171
|
|
|
|
|
|
|
rule_template_id_without_recursion_desc => $rule_template_without_recursion_desc->id, |
2172
|
|
|
|
|
|
|
rule_matches_all => $rule_template->matches_all, |
2173
|
|
|
|
|
|
|
rule_specifies_id => ($rule_template->specifies_value_for('id') || undef), |
2174
|
|
|
|
|
|
|
rule_template_is_id_only => $rule_template->is_id_only, |
2175
|
|
|
|
|
|
|
rule_template_specifies_value_for_subtype => $rule_template_specifies_value_for_subtype, |
2176
|
|
|
|
|
|
|
|
2177
|
|
|
|
|
|
|
recursion_desc => $rule_template->recursion_desc, |
2178
|
|
|
|
|
|
|
recurse_property_on_this_row => $recurse_property_on_this_row, |
2179
|
|
|
|
|
|
|
recurse_property_referencing_other_rows => $recurse_property_referencing_other_rows, |
2180
|
|
|
|
|
|
|
|
2181
|
|
|
|
|
|
|
loading_templates => $per_object_in_resultset_loading_detail, |
2182
|
|
|
|
|
|
|
|
2183
|
|
|
|
|
|
|
joins_across_data_sources => $joins_across_data_sources, |
2184
|
|
|
|
|
|
|
); |
2185
|
|
|
|
|
|
|
|
2186
|
143
|
|
|
|
|
1433
|
return $self; |
2187
|
|
|
|
|
|
|
} |
2188
|
|
|
|
|
|
|
|
2189
|
|
|
|
|
|
|
sub _init_default { |
2190
|
61
|
|
|
61
|
|
87
|
my $self = shift; |
2191
|
61
|
|
|
|
|
211
|
my $bx_template = $self->rule_template; |
2192
|
61
|
|
|
|
|
136
|
$self->{needs_further_boolexpr_evaluation_after_loading} = 1; |
2193
|
61
|
|
|
|
|
121
|
my $all_possible_headers = $self->{loading_templates}[0]{property_names}; |
2194
|
61
|
|
|
|
|
79
|
my $expected_headers; |
2195
|
61
|
|
|
|
|
171
|
my $class_meta = $bx_template->subject_class_name->__meta__; |
2196
|
61
|
|
|
|
|
139
|
for my $pname (@$all_possible_headers) { |
2197
|
185
|
|
|
|
|
693
|
my $pmeta = $class_meta->property($pname); |
2198
|
185
|
50
|
|
|
|
398
|
if ($pmeta->is_delegated) { |
2199
|
0
|
|
|
|
|
0
|
next; |
2200
|
|
|
|
|
|
|
} |
2201
|
185
|
|
|
|
|
358
|
push @$expected_headers, $pname; |
2202
|
|
|
|
|
|
|
} |
2203
|
61
|
|
|
|
|
123
|
$self->{loading_templates}[0]{property_names} = $expected_headers; |
2204
|
|
|
|
|
|
|
|
2205
|
61
|
100
|
|
|
|
198
|
if ($bx_template->subject_class_name->isa('UR::Value')) { |
2206
|
|
|
|
|
|
|
# Hack so the objects get blessed into the proper subclass in the Object Fabricator. |
2207
|
|
|
|
|
|
|
# This is necessary so every possible UR::Value subclass doesn't need its |
2208
|
|
|
|
|
|
|
# own "id" property defined. Without it, the data shows that these objects get |
2209
|
|
|
|
|
|
|
# loaded as the base UR::Value class (since its "id" is defined on UR:Value) |
2210
|
|
|
|
|
|
|
# and then would get automagically subclassed. |
2211
|
45
|
|
|
|
|
116
|
$self->{'loading_templates'}->[0]->{'final_class_name'} = $bx_template->subject_class_name |
2212
|
|
|
|
|
|
|
} |
2213
|
|
|
|
|
|
|
|
2214
|
61
|
|
|
|
|
146
|
return $self; |
2215
|
|
|
|
|
|
|
} |
2216
|
|
|
|
|
|
|
|
2217
|
|
|
|
|
|
|
|
2218
|
|
|
|
|
|
|
sub _init_remote_cache { |
2219
|
0
|
|
|
0
|
|
0
|
my $self = shift; |
2220
|
0
|
|
|
|
|
0
|
my $rule_template = $self->rule_template; |
2221
|
0
|
|
|
|
|
0
|
my $ds = $self->data_source; |
2222
|
|
|
|
|
|
|
|
2223
|
0
|
|
|
|
|
0
|
my $class_name = $rule_template->subject_class_name; |
2224
|
0
|
|
|
|
|
0
|
my $class_meta = $class_name->__meta__; |
2225
|
0
|
|
|
|
|
0
|
my $class_data = $ds->_get_class_data_for_loading($class_meta); |
2226
|
|
|
|
|
|
|
|
2227
|
0
|
|
|
|
|
0
|
my $recursion_desc = $rule_template->recursion_desc; |
2228
|
0
|
0
|
|
|
|
0
|
my $rule_template_without_recursion_desc = ($recursion_desc ? $rule_template->remove_filter('-recurse') : $rule_template); |
2229
|
0
|
|
|
|
|
0
|
my $rule_template_specifies_value_for_subtype; |
2230
|
0
|
|
|
|
|
0
|
my $sub_typing_property = $class_data->{'sub_typing_property'}; |
2231
|
0
|
0
|
|
|
|
0
|
if ($sub_typing_property) { |
2232
|
0
|
|
|
|
|
0
|
$rule_template_specifies_value_for_subtype = $rule_template->specifies_value_for($sub_typing_property) |
2233
|
|
|
|
|
|
|
} |
2234
|
|
|
|
|
|
|
|
2235
|
0
|
|
|
|
|
0
|
my @property_names = $class_name->__meta__->all_property_names; |
2236
|
|
|
|
|
|
|
|
2237
|
0
|
|
0
|
|
|
0
|
%$self = ( |
2238
|
|
|
|
|
|
|
%$self, |
2239
|
|
|
|
|
|
|
|
2240
|
|
|
|
|
|
|
select_clause => '', |
2241
|
|
|
|
|
|
|
select_hint => undef, |
2242
|
|
|
|
|
|
|
from_clause => '', |
2243
|
|
|
|
|
|
|
where_clause => '', |
2244
|
|
|
|
|
|
|
connect_by_clause => '', |
2245
|
|
|
|
|
|
|
order_by_clause => '', |
2246
|
|
|
|
|
|
|
|
2247
|
|
|
|
|
|
|
needs_further_boolexpr_evaluation_after_loading => undef, |
2248
|
|
|
|
|
|
|
loading_templates => [], |
2249
|
|
|
|
|
|
|
|
2250
|
|
|
|
|
|
|
sql_params => [], |
2251
|
|
|
|
|
|
|
filter_specs => [], |
2252
|
|
|
|
|
|
|
property_names_in_resultset_order => \@property_names, |
2253
|
|
|
|
|
|
|
properties_for_params => [], |
2254
|
|
|
|
|
|
|
|
2255
|
|
|
|
|
|
|
rule_template_id => $rule_template->id, |
2256
|
|
|
|
|
|
|
rule_template_without_recursion_desc => $rule_template_without_recursion_desc, |
2257
|
|
|
|
|
|
|
rule_template_id_without_recursion_desc => $rule_template_without_recursion_desc->id, |
2258
|
|
|
|
|
|
|
rule_matches_all => $rule_template->matches_all, |
2259
|
|
|
|
|
|
|
rule_specifies_id => ($rule_template->specifies_value_for('id') || undef), |
2260
|
|
|
|
|
|
|
rule_template_is_id_only => $rule_template->is_id_only, |
2261
|
|
|
|
|
|
|
rule_template_specifies_value_for_subtype => $rule_template_specifies_value_for_subtype, |
2262
|
|
|
|
|
|
|
|
2263
|
|
|
|
|
|
|
recursion_desc => undef, |
2264
|
|
|
|
|
|
|
recurse_property_on_this_row => undef, |
2265
|
|
|
|
|
|
|
recurse_property_referencing_other_rows => undef, |
2266
|
|
|
|
|
|
|
|
2267
|
|
|
|
|
|
|
%$class_data, |
2268
|
|
|
|
|
|
|
); |
2269
|
|
|
|
|
|
|
|
2270
|
0
|
|
|
|
|
0
|
return $self; |
2271
|
|
|
|
|
|
|
} |
2272
|
|
|
|
|
|
|
|
2273
|
|
|
|
|
|
|
sub order_by_column_list { |
2274
|
1447
|
|
|
1447
|
0
|
2040
|
my $self = shift; |
2275
|
|
|
|
|
|
|
|
2276
|
1447
|
|
|
|
|
3933
|
$self->_resolve_order_by_and_descending_data(); |
2277
|
1447
|
|
|
|
|
2739
|
return $self->{_order_by_column_list}; |
2278
|
|
|
|
|
|
|
} |
2279
|
|
|
|
|
|
|
|
2280
|
|
|
|
|
|
|
sub _resolve_order_by_and_descending_data { |
2281
|
3621
|
|
|
3621
|
|
3898
|
my $self = shift; |
2282
|
|
|
|
|
|
|
|
2283
|
3621
|
100
|
|
|
|
8467
|
unless ($self->{_order_by_column_list}) { |
2284
|
638
|
|
|
|
|
859
|
my %is_descending; |
2285
|
|
|
|
|
|
|
my @order_by_columns = |
2286
|
|
|
|
|
|
|
map { |
2287
|
|
|
|
|
|
|
m/^-(.*)/ |
2288
|
1078
|
100
|
|
|
|
3776
|
? $is_descending{$1} = $1 |
2289
|
|
|
|
|
|
|
: $_; |
2290
|
|
|
|
|
|
|
} |
2291
|
638
|
50
|
|
|
|
1047
|
@{ $self->order_by_columns || [] }; |
|
638
|
|
|
|
|
2251
|
|
2292
|
|
|
|
|
|
|
|
2293
|
638
|
|
|
|
|
1634
|
$self->{_order_by_column_list} = \@order_by_columns; |
2294
|
638
|
|
|
|
|
1660
|
$self->{_order_by_column_is_descending} = \%is_descending; |
2295
|
|
|
|
|
|
|
} |
2296
|
|
|
|
|
|
|
} |
2297
|
|
|
|
|
|
|
|
2298
|
|
|
|
|
|
|
sub order_by_column_is_descending { |
2299
|
2174
|
|
|
2174
|
0
|
2725
|
my($self, $column_name) = @_; |
2300
|
|
|
|
|
|
|
|
2301
|
2174
|
|
|
|
|
3938
|
$self->_resolve_order_by_and_descending_data(); |
2302
|
2174
|
|
|
|
|
5841
|
return $self->{_order_by_column_is_descending}->{$column_name}; |
2303
|
|
|
|
|
|
|
} |
2304
|
|
|
|
|
|
|
|
2305
|
|
|
|
|
|
|
sub property_meta_for_column { |
2306
|
2174
|
|
|
2174
|
0
|
2930
|
my($self, $table_and_column_name) = @_; |
2307
|
|
|
|
|
|
|
|
2308
|
2174
|
|
|
|
|
4081
|
$table_and_column_name = lc($table_and_column_name); |
2309
|
|
|
|
|
|
|
|
2310
|
2174
|
|
|
|
|
5539
|
my $data_source = $self->data_source(); |
2311
|
2174
|
|
|
|
|
7976
|
my ($table_name, $column_name) = $data_source->_resolve_table_and_column_from_column_name($table_and_column_name); |
2312
|
|
|
|
|
|
|
|
2313
|
2174
|
100
|
|
|
|
6336
|
if (my $join = $self->_get_alias_join($table_name)) { |
2314
|
|
|
|
|
|
|
# The given $table_name was actually a join alias |
2315
|
2
|
|
|
|
|
8
|
my $foreign_class_meta = $join->foreign_class->__meta__; |
2316
|
2
|
|
|
|
|
13
|
my $prop_name = $foreign_class_meta->property_for_column($column_name); |
2317
|
2
|
50
|
|
|
|
12
|
return $prop_name |
2318
|
|
|
|
|
|
|
? $foreign_class_meta->property_meta_for_name($prop_name) |
2319
|
|
|
|
|
|
|
: undef; |
2320
|
|
|
|
|
|
|
|
2321
|
|
|
|
|
|
|
} else { |
2322
|
2172
|
|
|
|
|
4864
|
my $class_meta = $self->class_name->__meta__; |
2323
|
2172
|
|
|
|
|
10188
|
my $prop_name = $class_meta->property_for_column($table_and_column_name); |
2324
|
2172
|
|
|
|
|
4687
|
return $class_meta->property_meta_for_name($prop_name); |
2325
|
|
|
|
|
|
|
} |
2326
|
|
|
|
|
|
|
} |
2327
|
|
|
|
|
|
|
|
2328
|
|
|
|
|
|
|
1; |
2329
|
|
|
|
|
|
|
|