| 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
|
|
|
|
|
|
|
|