| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package UR::DataSource; |
|
2
|
217
|
|
|
217
|
|
6174
|
use strict; |
|
|
217
|
|
|
|
|
297
|
|
|
|
217
|
|
|
|
|
5973
|
|
|
3
|
217
|
|
|
217
|
|
727
|
use warnings; |
|
|
217
|
|
|
|
|
306
|
|
|
|
217
|
|
|
|
|
9373
|
|
|
4
|
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
require UR; |
|
6
|
|
|
|
|
|
|
our $VERSION = "0.46"; # UR $VERSION; |
|
7
|
217
|
|
|
217
|
|
823
|
use Sys::Hostname; |
|
|
217
|
|
|
|
|
271
|
|
|
|
217
|
|
|
|
|
11873
|
|
|
8
|
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
{ |
|
10
|
217
|
|
|
217
|
|
763
|
no warnings 'once'; |
|
|
217
|
|
|
|
|
270
|
|
|
|
217
|
|
|
|
|
47516
|
|
|
11
|
|
|
|
|
|
|
*namespace = \&get_namespace; |
|
12
|
|
|
|
|
|
|
} |
|
13
|
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
UR::Object::Type->define( |
|
15
|
|
|
|
|
|
|
class_name => 'UR::DataSource', |
|
16
|
|
|
|
|
|
|
is_abstract => 1, |
|
17
|
|
|
|
|
|
|
doc => 'A logical database, independent of prod/dev/testing considerations or login details.', |
|
18
|
|
|
|
|
|
|
has => [ |
|
19
|
|
|
|
|
|
|
namespace => { calculate_from => ['id'] }, |
|
20
|
|
|
|
|
|
|
is_connected => { is => 'Boolean', default_value => 0, is_optional => 1, is_transient => 1 }, |
|
21
|
|
|
|
|
|
|
get_default_handle => { |
|
22
|
|
|
|
|
|
|
is_calculated => 1, |
|
23
|
|
|
|
|
|
|
is_constant => 1, |
|
24
|
|
|
|
|
|
|
doc => 'Underlying handle for this datasource', |
|
25
|
|
|
|
|
|
|
calculate => '$self->create_default_handle_wrapper', |
|
26
|
|
|
|
|
|
|
}, |
|
27
|
|
|
|
|
|
|
], |
|
28
|
|
|
|
|
|
|
valid_signals => ['precreate_handle', 'create_handle', 'predisconnect_handle', 'disconnect_handle' ], |
|
29
|
|
|
|
|
|
|
); |
|
30
|
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
our @CARP_NOT = qw(UR::Context UR::DataSource::QueryPlan); |
|
32
|
|
|
|
|
|
|
|
|
33
|
12
|
|
|
12
|
0
|
56
|
sub define { shift->__define__(@_) } |
|
34
|
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
sub get_namespace { |
|
36
|
1667
|
|
|
1667
|
0
|
4611
|
my $class = shift->class; |
|
37
|
1667
|
|
|
|
|
5345
|
return substr($class,0,index($class,"::DataSource")); |
|
38
|
|
|
|
|
|
|
} |
|
39
|
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
sub get_name { |
|
41
|
0
|
|
|
0
|
0
|
0
|
my $class = shift->class; |
|
42
|
0
|
|
|
|
|
0
|
return lc(substr($class,index($class,"::DataSource")+14)); |
|
43
|
|
|
|
|
|
|
} |
|
44
|
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
# The default used to be to force table/column/constraint/etc names to |
|
46
|
|
|
|
|
|
|
# upper case when storing them in the MetaDB, and in the column_name |
|
47
|
|
|
|
|
|
|
# metadata for properties. The new behavior is to just use whatever the |
|
48
|
|
|
|
|
|
|
# database supplies us when interrogating the data dictionary. |
|
49
|
|
|
|
|
|
|
# For datasources/clases that still need the old behavior, override this |
|
50
|
|
|
|
|
|
|
# to make the column_name metadata for properties forced to upper-case |
|
51
|
3012
|
|
|
3012
|
0
|
6964
|
sub table_and_column_names_are_upper_case { 0; } |
|
52
|
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
# Basic, dumb data sources do not support joins within a single |
|
55
|
|
|
|
|
|
|
# query. Instead the Context logic can perform a cross datasource |
|
56
|
|
|
|
|
|
|
# join within irs own code |
|
57
|
3
|
|
|
3
|
0
|
19
|
sub does_support_joins { 0; } |
|
58
|
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
# Many data sources do not support limit and offset. |
|
60
|
|
|
|
|
|
|
sub does_support_limit_offset { |
|
61
|
|
|
|
|
|
|
#my($self, $query_plan) = @_; |
|
62
|
0
|
|
|
0
|
0
|
0
|
0 |
|
63
|
|
|
|
|
|
|
} |
|
64
|
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
# Most datasources do not support recursive queries |
|
66
|
|
|
|
|
|
|
# Oracle and Postgres do, but in different ways |
|
67
|
|
|
|
|
|
|
# For data sources without support, it'll have to do multiple queries |
|
68
|
|
|
|
|
|
|
# to get all the data |
|
69
|
10
|
|
|
10
|
0
|
31
|
sub does_support_recursive_queries { ''; } |
|
70
|
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
{ |
|
73
|
217
|
|
|
217
|
|
893
|
no warnings 'once'; |
|
|
217
|
|
|
|
|
272
|
|
|
|
217
|
|
|
|
|
194009
|
|
|
74
|
|
|
|
|
|
|
*create_dbh = \&create_default_handle_wrapper; |
|
75
|
|
|
|
|
|
|
} |
|
76
|
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
sub create_default_handle_wrapper { |
|
78
|
549
|
|
|
549
|
0
|
2036
|
my $self = UR::Util::object(shift); |
|
79
|
|
|
|
|
|
|
|
|
80
|
549
|
|
|
|
|
2837
|
$self->__signal_observers__('precreate_handle'); |
|
81
|
549
|
|
|
|
|
2673
|
my $h = $self->create_default_handle; |
|
82
|
547
|
|
|
|
|
1843
|
$self->__signal_observers__('create_handle', $h); |
|
83
|
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
# Hack - This is to avoid infinite recursion in the case where the |
|
85
|
|
|
|
|
|
|
# handle initializers below try to get the hadle by calling $ds->get_default_handle. |
|
86
|
|
|
|
|
|
|
# The cached/calculated accessor code will look in this hash key and |
|
87
|
|
|
|
|
|
|
# return the handle instead of recursing back into the handle creation, and |
|
88
|
|
|
|
|
|
|
# back to here |
|
89
|
547
|
|
|
|
|
1275
|
$self->{get_default_handle} = $h; |
|
90
|
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
# Backward compatability for older code that still uses _init_created_dbh |
|
92
|
547
|
100
|
|
|
|
2310
|
if ($self->can('_init_created_dbh')) { |
|
93
|
172
|
|
|
|
|
2342
|
$self->_init_created_dbh($h); |
|
94
|
|
|
|
|
|
|
} else { |
|
95
|
375
|
|
|
|
|
27088
|
$self->init_created_handle($h); |
|
96
|
|
|
|
|
|
|
} |
|
97
|
|
|
|
|
|
|
|
|
98
|
547
|
|
|
|
|
1863
|
return $h; |
|
99
|
|
|
|
|
|
|
} |
|
100
|
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
# basic, dumb datasources do not have a handle |
|
102
|
0
|
|
|
0
|
0
|
0
|
sub create_default_handle { undef } |
|
103
|
|
|
|
0
|
0
|
|
sub disconnect { } |
|
104
|
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
# derived classes can implement this to do extra initialization after the |
|
106
|
|
|
|
|
|
|
# handle is created |
|
107
|
375
|
|
|
375
|
0
|
435
|
sub init_created_handle { 1; } |
|
108
|
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
# Peek into the object and see if there's anything in 'get_default_handle' without actually |
|
110
|
|
|
|
|
|
|
# creating a handle |
|
111
|
|
|
|
|
|
|
*has_default_dbh = \&has_default_handle; |
|
112
|
|
|
|
|
|
|
sub has_default_handle { |
|
113
|
649
|
|
|
649
|
0
|
1451
|
my $self = UR::Util::object(shift); |
|
114
|
649
|
|
|
|
|
2294
|
return exists($self->{get_default_handle}); |
|
115
|
|
|
|
|
|
|
} |
|
116
|
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
*disconnect_default_dbh = \&disconnect_default_handle; |
|
118
|
|
|
|
|
|
|
sub disconnect_default_handle { |
|
119
|
13
|
|
|
13
|
0
|
857
|
my $self = shift; |
|
120
|
|
|
|
|
|
|
|
|
121
|
13
|
100
|
|
|
|
57
|
if ($self->has_default_handle) { |
|
122
|
3
|
|
|
|
|
14
|
$self->__signal_observers__('predisconnect_handle'); |
|
123
|
3
|
|
|
|
|
19
|
$self->disconnect(); |
|
124
|
3
|
|
|
|
|
43
|
$self->__signal_observers__('disconnect_handle'); |
|
125
|
|
|
|
|
|
|
} |
|
126
|
13
|
|
|
|
|
31
|
1; |
|
127
|
|
|
|
|
|
|
} |
|
128
|
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
our $use_dummy_autogenerated_ids; |
|
130
|
|
|
|
|
|
|
*use_dummy_autogenerated_ids = \$ENV{UR_USE_DUMMY_AUTOGENERATED_IDS}; |
|
131
|
|
|
|
|
|
|
sub use_dummy_autogenerated_ids { |
|
132
|
|
|
|
|
|
|
# This allows the saved SQL from sync database to be comparable across executions. |
|
133
|
|
|
|
|
|
|
# It also |
|
134
|
162
|
|
|
162
|
1
|
246
|
my $class = shift; |
|
135
|
162
|
50
|
|
|
|
486
|
if (@_) { |
|
136
|
0
|
|
|
|
|
0
|
($use_dummy_autogenerated_ids) = @_; |
|
137
|
|
|
|
|
|
|
} |
|
138
|
162
|
|
50
|
|
|
1607
|
$use_dummy_autogenerated_ids ||= 0; # Replace undef with 0 |
|
139
|
162
|
|
|
|
|
508
|
return $use_dummy_autogenerated_ids; |
|
140
|
|
|
|
|
|
|
} |
|
141
|
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
our $last_dummy_autogenerated_id; |
|
143
|
|
|
|
|
|
|
sub next_dummy_autogenerated_id { |
|
144
|
0
|
0
|
|
0
|
1
|
0
|
unless($last_dummy_autogenerated_id) { |
|
145
|
0
|
|
|
|
|
0
|
my $hostname = hostname(); |
|
146
|
0
|
|
|
|
|
0
|
$hostname =~ /(\d+)/; |
|
147
|
0
|
0
|
|
|
|
0
|
my $id = $1 ? $1 : 1; |
|
148
|
0
|
|
|
|
|
0
|
$last_dummy_autogenerated_id = ($id * -10_000_000) - ($$ * 1_000); |
|
149
|
|
|
|
|
|
|
} |
|
150
|
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
#limit id to fit within 11 characters |
|
152
|
0
|
|
|
|
|
0
|
($last_dummy_autogenerated_id) = $last_dummy_autogenerated_id =~ m/(-\d{1,10})/; |
|
153
|
|
|
|
|
|
|
|
|
154
|
0
|
|
|
|
|
0
|
return --$last_dummy_autogenerated_id; |
|
155
|
|
|
|
|
|
|
} |
|
156
|
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
sub autogenerate_new_object_id_for_class_name_and_rule { |
|
158
|
0
|
|
|
0
|
1
|
0
|
my $ds = shift; |
|
159
|
|
|
|
|
|
|
|
|
160
|
0
|
0
|
|
|
|
0
|
if (ref $ds) { |
|
161
|
0
|
|
|
|
|
0
|
$ds = ref($ds) . " ID " . $ds->id; |
|
162
|
|
|
|
|
|
|
} |
|
163
|
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
# Maybe we could use next_dummy_autogenerated_id instead? |
|
165
|
0
|
|
|
|
|
0
|
die "Data source $ds did not implement autogenerate_new_object_id_for_class_name_and_rule()"; |
|
166
|
|
|
|
|
|
|
} |
|
167
|
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
# UR::Context needs to know if a data source supports savepoints |
|
169
|
|
|
|
|
|
|
sub can_savepoint { |
|
170
|
0
|
|
|
0
|
0
|
0
|
my $class = ref($_[0]); |
|
171
|
0
|
|
|
|
|
0
|
die "Class $class didn't supply can_savepoint()"; |
|
172
|
|
|
|
|
|
|
} |
|
173
|
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
sub set_savepoint { |
|
175
|
0
|
|
|
0
|
0
|
0
|
my $class = ref($_[0]); |
|
176
|
0
|
|
|
|
|
0
|
die "Class $class didn't supply set_savepoint, but can_savepoint is true"; |
|
177
|
|
|
|
|
|
|
} |
|
178
|
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
sub rollback_to_savepoint { |
|
180
|
0
|
|
|
0
|
0
|
0
|
my $class = ref($_[0]); |
|
181
|
0
|
|
|
|
|
0
|
die "Class $class didn't supply rollback_to_savepoint, but can_savepoint is true"; |
|
182
|
|
|
|
|
|
|
} |
|
183
|
|
|
|
|
|
|
|
|
184
|
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
sub _get_class_data_for_loading { |
|
186
|
3897
|
|
|
3897
|
|
5875
|
my ($self, $class_meta) = @_; |
|
187
|
3897
|
|
|
|
|
6361
|
my $class_data = $class_meta->{loading_data_cache}; |
|
188
|
3897
|
50
|
|
|
|
8132
|
unless ($class_data) { |
|
189
|
3897
|
|
|
|
|
13346
|
$class_data = $self->_generate_class_data_for_loading($class_meta); |
|
190
|
|
|
|
|
|
|
} |
|
191
|
3897
|
|
|
|
|
10900
|
return $class_data; |
|
192
|
|
|
|
|
|
|
} |
|
193
|
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
sub _resolve_query_plan { |
|
195
|
3838
|
|
|
3838
|
|
4605
|
my ($self, $rule_template) = @_; |
|
196
|
3838
|
|
|
|
|
14996
|
my $qp = UR::DataSource::QueryPlan->get( |
|
197
|
|
|
|
|
|
|
rule_template => $rule_template, |
|
198
|
|
|
|
|
|
|
data_source => $self, |
|
199
|
|
|
|
|
|
|
); |
|
200
|
3838
|
100
|
|
|
|
12602
|
$qp->_init() unless $qp->_is_initialized; |
|
201
|
3838
|
|
|
|
|
9845
|
return $qp; |
|
202
|
|
|
|
|
|
|
} |
|
203
|
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
# Child classes can override this to return a different datasource |
|
205
|
|
|
|
|
|
|
# depending on the rule passed in |
|
206
|
|
|
|
|
|
|
sub resolve_data_sources_for_rule { |
|
207
|
4488
|
|
|
4488
|
1
|
7352
|
return $_[0]; |
|
208
|
|
|
|
|
|
|
} |
|
209
|
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
sub _generate_class_data_for_loading { |
|
211
|
3897
|
|
|
3897
|
|
4594
|
my ($self, $class_meta) = @_; |
|
212
|
|
|
|
|
|
|
|
|
213
|
3897
|
|
|
|
|
11375
|
my $class_name = $class_meta->class_name; |
|
214
|
3897
|
|
|
|
|
17464
|
my $ghost_class = $class_name->ghost_class; |
|
215
|
|
|
|
|
|
|
|
|
216
|
3897
|
|
|
|
|
15288
|
my @all_id_property_names = $class_meta->all_id_property_names(); |
|
217
|
3897
|
|
|
|
|
11702
|
my @id_properties = $class_meta->id_property_names; |
|
218
|
3897
|
|
|
|
|
13639
|
my $id_property_sorter = $class_meta->id_property_sorter; |
|
219
|
3897
|
|
|
|
|
9518
|
my @class_hierarchy = ($class_meta->class_name,$class_meta->ancestry_class_names); |
|
220
|
|
|
|
|
|
|
|
|
221
|
3897
|
|
|
|
|
12581
|
my @parent_class_objects = $class_meta->ancestry_class_metas; |
|
222
|
3897
|
|
|
|
|
5238
|
my $sub_classification_method_name; |
|
223
|
3897
|
|
|
|
|
4302
|
my ($sub_classification_meta_class_name, $subclassify_by); |
|
224
|
|
|
|
|
|
|
|
|
225
|
0
|
|
|
|
|
0
|
my @all_properties; |
|
226
|
0
|
|
|
|
|
0
|
my $first_table_name; |
|
227
|
0
|
|
|
|
|
0
|
my %seen; |
|
228
|
3897
|
|
|
|
|
6693
|
for my $co ( $class_meta, @parent_class_objects ) { |
|
229
|
12487
|
100
|
|
|
|
37098
|
next if ($seen{ $co->id })++; |
|
230
|
12485
|
|
100
|
|
|
32032
|
my $table_name = $co->table_name || '__default__'; |
|
231
|
|
|
|
|
|
|
|
|
232
|
12485
|
|
66
|
|
|
26392
|
$first_table_name ||= $table_name; |
|
233
|
12485
|
|
100
|
|
|
40477
|
$sub_classification_method_name ||= $co->sub_classification_method_name; |
|
234
|
12485
|
|
33
|
|
|
38758
|
$sub_classification_meta_class_name ||= $co->sub_classification_meta_class_name; |
|
235
|
12485
|
|
100
|
|
|
38638
|
$subclassify_by ||= $co->subclassify_by; |
|
236
|
|
|
|
|
|
|
|
|
237
|
12485
|
|
|
29515
|
|
42812
|
my $sort_sub = sub ($$) { return $_[0]->property_name cmp $_[1]->property_name }; |
|
|
29515
|
|
|
|
|
43495
|
|
|
238
|
12485
|
|
|
|
|
32681
|
push @all_properties, map { [$co, $_, $table_name, 0]} sort $sort_sub UR::Object::Property->get(class_name => $co->class_name); |
|
|
24210
|
|
|
|
|
68205
|
|
|
239
|
|
|
|
|
|
|
} |
|
240
|
|
|
|
|
|
|
|
|
241
|
3897
|
|
|
|
|
12019
|
my $sub_typing_property = $class_meta->subclassify_by; |
|
242
|
|
|
|
|
|
|
|
|
243
|
3897
|
|
|
|
|
10322
|
my $class_table_name = $class_meta->table_name; |
|
244
|
|
|
|
|
|
|
|
|
245
|
3897
|
|
|
|
|
14950
|
my $class_data = { |
|
246
|
|
|
|
|
|
|
class_name => $class_name, |
|
247
|
|
|
|
|
|
|
ghost_class => $class_name->ghost_class, |
|
248
|
|
|
|
|
|
|
|
|
249
|
|
|
|
|
|
|
parent_class_objects => [$class_meta->ancestry_class_metas], ## |
|
250
|
|
|
|
|
|
|
sub_classification_method_name => $sub_classification_method_name, |
|
251
|
|
|
|
|
|
|
sub_classification_meta_class_name => $sub_classification_meta_class_name, |
|
252
|
|
|
|
|
|
|
subclassify_by => $subclassify_by, |
|
253
|
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
all_properties => \@all_properties, |
|
255
|
|
|
|
|
|
|
all_id_property_names => [$class_meta->all_id_property_names()], |
|
256
|
|
|
|
|
|
|
id_properties => [$class_meta->id_property_names], |
|
257
|
|
|
|
|
|
|
id_property_sorter => $class_meta->id_property_sorter, |
|
258
|
|
|
|
|
|
|
|
|
259
|
|
|
|
|
|
|
sub_typing_property => $sub_typing_property, |
|
260
|
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
# these seem like they go in the RDBMS subclass, but for now the |
|
262
|
|
|
|
|
|
|
# "table" concept is stretched to mean any valid structure identifier |
|
263
|
|
|
|
|
|
|
# within the datasource. |
|
264
|
|
|
|
|
|
|
first_table_name => $first_table_name, |
|
265
|
|
|
|
|
|
|
class_table_name => $class_table_name, |
|
266
|
|
|
|
|
|
|
}; |
|
267
|
|
|
|
|
|
|
|
|
268
|
3897
|
|
|
|
|
20329
|
return $class_data; |
|
269
|
|
|
|
|
|
|
} |
|
270
|
|
|
|
|
|
|
|
|
271
|
|
|
|
|
|
|
sub _generate_loading_templates_arrayref { |
|
272
|
|
|
|
|
|
|
# Each entry represents a table alias in the query. |
|
273
|
|
|
|
|
|
|
# This accounts for different tables, or multiple occurrances |
|
274
|
|
|
|
|
|
|
# of the same table in a join, by grouping by alias instead of |
|
275
|
|
|
|
|
|
|
# table. |
|
276
|
|
|
|
|
|
|
|
|
277
|
785
|
|
|
785
|
|
1249
|
my $class = shift; |
|
278
|
785
|
|
|
|
|
1080
|
my $db_cols = shift; |
|
279
|
785
|
|
|
|
|
1048
|
my $obj_joins = shift; |
|
280
|
785
|
|
|
|
|
1133
|
my $bxt = shift; |
|
281
|
|
|
|
|
|
|
|
|
282
|
217
|
|
|
217
|
|
1116
|
use strict; |
|
|
217
|
|
|
|
|
294
|
|
|
|
217
|
|
|
|
|
3966
|
|
|
283
|
217
|
|
|
217
|
|
714
|
use warnings; |
|
|
217
|
|
|
|
|
264
|
|
|
|
217
|
|
|
|
|
443360
|
|
|
284
|
|
|
|
|
|
|
|
|
285
|
785
|
|
|
|
|
1003
|
my %obj_joins_by_source_alias; |
|
286
|
785
|
|
|
|
|
1244
|
if (0) { # ($obj_joins) { |
|
287
|
|
|
|
|
|
|
my @obj_joins = @$obj_joins; |
|
288
|
|
|
|
|
|
|
while (@obj_joins) { |
|
289
|
|
|
|
|
|
|
my $foreign_alias = shift @obj_joins; |
|
290
|
|
|
|
|
|
|
my $data = shift @obj_joins; |
|
291
|
|
|
|
|
|
|
for my $foreign_property_name (sort keys %$data) { |
|
292
|
|
|
|
|
|
|
next if $foreign_property_name eq '-is_required'; |
|
293
|
|
|
|
|
|
|
|
|
294
|
|
|
|
|
|
|
my $source_alias = $data->{$foreign_property_name}{'link_alias'}; |
|
295
|
|
|
|
|
|
|
my $detail = $obj_joins_by_source_alias{$source_alias}{$foreign_alias} ||= {}; |
|
296
|
|
|
|
|
|
|
# warnings come from the above because we don't have 'link_alias' in filters. |
|
297
|
|
|
|
|
|
|
|
|
298
|
|
|
|
|
|
|
my $source_property_name = $data->{$foreign_property_name}{'link_property_name'}; |
|
299
|
|
|
|
|
|
|
if ($source_property_name) { |
|
300
|
|
|
|
|
|
|
# join |
|
301
|
|
|
|
|
|
|
my $links = $detail->{links} ||= []; |
|
302
|
|
|
|
|
|
|
push @$links, $foreign_property_name, $source_property_name; |
|
303
|
|
|
|
|
|
|
} |
|
304
|
|
|
|
|
|
|
|
|
305
|
|
|
|
|
|
|
if (exists $data->{value}) { |
|
306
|
|
|
|
|
|
|
# filter |
|
307
|
|
|
|
|
|
|
my $operator = $data->{operator}; |
|
308
|
|
|
|
|
|
|
my $value = $data->{value}; |
|
309
|
|
|
|
|
|
|
my $filter = $detail->{filter} ||= []; |
|
310
|
|
|
|
|
|
|
my $key = $foreign_property_name; |
|
311
|
|
|
|
|
|
|
$key .= ' ' . $operator if $operator; |
|
312
|
|
|
|
|
|
|
push @$filter, $key, $value; |
|
313
|
|
|
|
|
|
|
} |
|
314
|
|
|
|
|
|
|
} |
|
315
|
|
|
|
|
|
|
} |
|
316
|
|
|
|
|
|
|
} |
|
317
|
|
|
|
|
|
|
else { |
|
318
|
|
|
|
|
|
|
#Carp::cluck("no obj joins???"); |
|
319
|
|
|
|
|
|
|
} |
|
320
|
|
|
|
|
|
|
|
|
321
|
785
|
|
|
|
|
1108
|
my %templates; |
|
322
|
785
|
|
|
|
|
1131
|
my $pos = 0; |
|
323
|
785
|
|
|
|
|
1008
|
my @templates; |
|
324
|
|
|
|
|
|
|
my %alias_object_num; |
|
325
|
785
|
|
|
|
|
1566
|
for my $col_data (@$db_cols) { |
|
326
|
3599
|
|
|
|
|
3970
|
my ($class_obj, $prop, $table_alias, $object_num) = @$col_data; |
|
327
|
3599
|
50
|
|
|
|
5140
|
unless (defined $object_num) { |
|
328
|
0
|
|
|
|
|
0
|
die "No object num for loading template data?!"; |
|
329
|
|
|
|
|
|
|
} |
|
330
|
|
|
|
|
|
|
#Carp::confess() unless $table_alias; |
|
331
|
3599
|
|
|
|
|
3309
|
my $template = $templates[$object_num]; |
|
332
|
3599
|
100
|
|
|
|
5180
|
unless ($template) { |
|
333
|
906
|
|
|
|
|
2875
|
$template = { |
|
334
|
|
|
|
|
|
|
object_num => $object_num, |
|
335
|
|
|
|
|
|
|
table_alias => $table_alias, |
|
336
|
|
|
|
|
|
|
data_class_name => $class_obj->class_name, |
|
337
|
|
|
|
|
|
|
final_class_name => $class_obj->class_name, |
|
338
|
|
|
|
|
|
|
property_names => [], |
|
339
|
|
|
|
|
|
|
column_positions => [], |
|
340
|
|
|
|
|
|
|
id_property_names => undef, |
|
341
|
|
|
|
|
|
|
id_column_positions => [], |
|
342
|
|
|
|
|
|
|
id_resolver => undef, # subref |
|
343
|
|
|
|
|
|
|
}; |
|
344
|
906
|
|
|
|
|
1991
|
$templates[$object_num] = $template; |
|
345
|
906
|
|
|
|
|
1859
|
$alias_object_num{$table_alias} = $object_num; |
|
346
|
|
|
|
|
|
|
} |
|
347
|
3599
|
|
|
|
|
2542
|
push @{ $template->{property_names} }, $prop->property_name; |
|
|
3599
|
|
|
|
|
6727
|
|
|
348
|
3599
|
|
|
|
|
3068
|
push @{ $template->{column_positions} }, $pos; |
|
|
3599
|
|
|
|
|
3666
|
|
|
349
|
3599
|
|
|
|
|
3951
|
$pos++; |
|
350
|
|
|
|
|
|
|
} |
|
351
|
|
|
|
|
|
|
|
|
352
|
|
|
|
|
|
|
# remove joins that resulted in no template, such as when it was to a table-less class |
|
353
|
785
|
|
|
|
|
1435
|
@templates = grep { $_ } @templates; |
|
|
906
|
|
|
|
|
2025
|
|
|
354
|
|
|
|
|
|
|
|
|
355
|
|
|
|
|
|
|
# Post-process the template objects a bit to get the exact id positions. |
|
356
|
785
|
|
|
|
|
1470
|
for my $template (@templates) { |
|
357
|
906
|
|
|
|
|
1089
|
my @id_property_names; |
|
358
|
906
|
|
|
|
|
25629
|
for my $id_class_name ($template->{data_class_name}, $template->{data_class_name}->inheritance) { |
|
359
|
906
|
|
|
|
|
3565
|
my $id_class_obj = UR::Object::Type->get(class_name => $id_class_name); |
|
360
|
906
|
50
|
|
|
|
3469
|
last if @id_property_names = $id_class_obj->id_property_names; |
|
361
|
|
|
|
|
|
|
} |
|
362
|
906
|
|
|
|
|
2073
|
$template->{id_property_names} = \@id_property_names; |
|
363
|
|
|
|
|
|
|
|
|
364
|
906
|
|
|
|
|
1184
|
my @id_column_positions; |
|
365
|
906
|
|
|
|
|
1705
|
for my $id_property_name (@id_property_names) { |
|
366
|
1364
|
|
|
|
|
1538
|
for my $n (0..$#{ $template->{property_names} }) { |
|
|
1364
|
|
|
|
|
3703
|
|
|
367
|
3584
|
100
|
|
|
|
6310
|
if ($template->{property_names}[$n] eq $id_property_name) { |
|
368
|
1364
|
|
|
|
|
2138
|
push @id_column_positions, $template->{column_positions}[$n]; |
|
369
|
1364
|
|
|
|
|
1981
|
last; |
|
370
|
|
|
|
|
|
|
} |
|
371
|
|
|
|
|
|
|
} |
|
372
|
|
|
|
|
|
|
} |
|
373
|
906
|
|
|
|
|
1632
|
$template->{id_column_positions} = \@id_column_positions; |
|
374
|
|
|
|
|
|
|
|
|
375
|
906
|
100
|
|
|
|
2653
|
if (@id_column_positions == 1) { |
|
|
|
50
|
|
|
|
|
|
|
376
|
|
|
|
|
|
|
$template->{id_resolver} = sub { |
|
377
|
0
|
|
|
0
|
|
0
|
return $_[0][$id_column_positions[0]]; |
|
378
|
|
|
|
|
|
|
} |
|
379
|
679
|
|
|
|
|
3120
|
} |
|
380
|
|
|
|
|
|
|
elsif (@id_column_positions > 1) { |
|
381
|
227
|
|
|
|
|
464
|
my $class_name = $template->{data_class_name}; |
|
382
|
|
|
|
|
|
|
$template->{id_resolver} = sub { |
|
383
|
0
|
|
|
0
|
|
0
|
my $self = shift; |
|
384
|
0
|
|
|
|
|
0
|
return $class_name->__meta__->resolve_composite_id_from_ordered_values(@$self[@id_column_positions]); |
|
385
|
|
|
|
|
|
|
} |
|
386
|
227
|
|
|
|
|
1481
|
} |
|
387
|
|
|
|
|
|
|
else { |
|
388
|
|
|
|
|
|
|
Carp::croak("Can't determine which columns will hold the ID property data for class " |
|
389
|
|
|
|
|
|
|
. $template->{data_class_name} . ". It's ID properties are (" . join(', ', @id_property_names) |
|
390
|
0
|
|
|
|
|
0
|
. ") which do not appear in the class' property list (" . join(', ', @{$template->{'property_names'}}).")"); |
|
|
0
|
|
|
|
|
0
|
|
|
391
|
|
|
|
|
|
|
} |
|
392
|
|
|
|
|
|
|
|
|
393
|
906
|
|
|
|
|
1855
|
my $source_alias = $template->{table_alias}; |
|
394
|
906
|
|
|
|
|
1612
|
if (0 and my $join_data_for_source_table = $obj_joins_by_source_alias{$source_alias}) { |
|
395
|
|
|
|
|
|
|
# there are joins which come from this entity to other entities |
|
396
|
|
|
|
|
|
|
# as these entities are loaded, remember the individual queries covered by this object returning |
|
397
|
|
|
|
|
|
|
# NOTE: when we join a <> b, we remember that we've loaded all of the b for a when _a_ loads, not b, |
|
398
|
|
|
|
|
|
|
# since it's possible that there ar zero of b, and we don't want to perform the query for b |
|
399
|
|
|
|
|
|
|
my $source_object_num = $template->{object_num}; |
|
400
|
|
|
|
|
|
|
my $source_class_name = $template->{data_class_name}; |
|
401
|
|
|
|
|
|
|
my $next_joins = $template->{next_joins} ||= []; |
|
402
|
|
|
|
|
|
|
for my $foreign_alias (keys %$join_data_for_source_table) { |
|
403
|
|
|
|
|
|
|
my $foreign_object_num = $alias_object_num{$foreign_alias}; |
|
404
|
|
|
|
|
|
|
Carp::confess("no alias for $foreign_alias?") if not defined $foreign_object_num; |
|
405
|
|
|
|
|
|
|
my $foreign_template = $templates[$foreign_object_num]; |
|
406
|
|
|
|
|
|
|
my $foreign_class_name = $foreign_template->{data_class_name}; |
|
407
|
|
|
|
|
|
|
|
|
408
|
|
|
|
|
|
|
my $join_data = $join_data_for_source_table->{$foreign_alias}; |
|
409
|
|
|
|
|
|
|
my %links = map { $_ ? @$_ : () } $join_data->{links}; |
|
410
|
|
|
|
|
|
|
my %filters = map { $_ ? @$_ : () } $join_data->{filters}; |
|
411
|
|
|
|
|
|
|
|
|
412
|
|
|
|
|
|
|
my @keys = sort (keys %links, keys %filters); |
|
413
|
|
|
|
|
|
|
my @value_position_source_property; |
|
414
|
|
|
|
|
|
|
for (my $n = 0; $n < @keys; $n++) { |
|
415
|
|
|
|
|
|
|
my $key = $keys[$n]; |
|
416
|
|
|
|
|
|
|
if ($links{$key} and $filters{$key}) { |
|
417
|
|
|
|
|
|
|
Carp::confess("unexpected same key $key in filters and joins"); |
|
418
|
|
|
|
|
|
|
} |
|
419
|
|
|
|
|
|
|
my $source_property_name = $links{$key}; |
|
420
|
|
|
|
|
|
|
next unless $source_property_name; |
|
421
|
|
|
|
|
|
|
push @value_position_source_property, $n, $source_property_name; |
|
422
|
|
|
|
|
|
|
} |
|
423
|
|
|
|
|
|
|
my $bx = $foreign_class_name->define_boolexpr(map { $_ => $filters{$_} } @keys); |
|
424
|
|
|
|
|
|
|
my ($bxt, @values) = $bx->template_and_values(); |
|
425
|
|
|
|
|
|
|
push @$next_joins, [ $bxt->id, \@values, \@value_position_source_property ]; |
|
426
|
|
|
|
|
|
|
} |
|
427
|
|
|
|
|
|
|
} |
|
428
|
|
|
|
|
|
|
} |
|
429
|
|
|
|
|
|
|
|
|
430
|
785
|
|
|
|
|
3046
|
return \@templates; |
|
431
|
|
|
|
|
|
|
} |
|
432
|
|
|
|
|
|
|
|
|
433
|
|
|
|
|
|
|
sub create_iterator_closure_for_rule_template_and_values { |
|
434
|
0
|
|
|
0
|
1
|
0
|
my ($self, $rule_template, @values) = @_; |
|
435
|
0
|
|
|
|
|
0
|
my $rule = $rule_template->get_rule_for_values(@values); |
|
436
|
0
|
|
|
|
|
0
|
return $self->create_iterator_closure_for_rule($rule); |
|
437
|
|
|
|
|
|
|
} |
|
438
|
|
|
|
|
|
|
|
|
439
|
|
|
|
|
|
|
sub _reclassify_object_loading_info_for_new_class { |
|
440
|
115
|
|
|
115
|
|
138
|
my $self = shift; |
|
441
|
115
|
|
|
|
|
130
|
my $loading_info = shift; |
|
442
|
115
|
|
|
|
|
138
|
my $new_class = shift; |
|
443
|
|
|
|
|
|
|
|
|
444
|
115
|
|
|
|
|
133
|
my $new_info; |
|
445
|
115
|
|
|
|
|
338
|
%$new_info = %$loading_info; |
|
446
|
|
|
|
|
|
|
|
|
447
|
115
|
|
|
|
|
247
|
foreach my $template_id (keys %$loading_info) { |
|
448
|
|
|
|
|
|
|
|
|
449
|
150
|
|
|
|
|
190
|
my $target_class_rules = $loading_info->{$template_id}; |
|
450
|
150
|
|
|
|
|
268
|
foreach my $rule_id (keys %$target_class_rules) { |
|
451
|
163
|
|
|
|
|
520
|
my $pos = index($rule_id,'/'); |
|
452
|
163
|
|
|
|
|
571
|
$new_info->{$template_id}->{$new_class . "/" . substr($rule_id,$pos+1)} = 1; |
|
453
|
|
|
|
|
|
|
} |
|
454
|
|
|
|
|
|
|
} |
|
455
|
|
|
|
|
|
|
|
|
456
|
115
|
|
|
|
|
231
|
return $new_info; |
|
457
|
|
|
|
|
|
|
} |
|
458
|
|
|
|
|
|
|
|
|
459
|
|
|
|
|
|
|
sub _get_object_loading_info { |
|
460
|
115
|
|
|
115
|
|
174
|
my $self = shift; |
|
461
|
115
|
|
|
|
|
134
|
my $obj = shift; |
|
462
|
115
|
|
|
|
|
138
|
my %param_load_hash; |
|
463
|
115
|
50
|
|
|
|
298
|
if ($obj->{'__load'}) { |
|
464
|
115
|
|
|
|
|
150
|
while( my($template_id, $rules) = each %{ $obj->{'__load'} } ) { |
|
|
252
|
|
|
|
|
787
|
|
|
465
|
137
|
|
|
|
|
307
|
foreach my $rule_id ( keys %$rules ) { |
|
466
|
137
|
|
|
|
|
445
|
$param_load_hash{$template_id}->{$rule_id} = $UR::Context::all_params_loaded->{$template_id}->{$rule_id}; |
|
467
|
|
|
|
|
|
|
} |
|
468
|
|
|
|
|
|
|
} |
|
469
|
|
|
|
|
|
|
} |
|
470
|
115
|
|
|
|
|
285
|
return \%param_load_hash; |
|
471
|
|
|
|
|
|
|
} |
|
472
|
|
|
|
|
|
|
|
|
473
|
|
|
|
|
|
|
|
|
474
|
|
|
|
|
|
|
sub _add_object_loading_info { |
|
475
|
56
|
|
|
56
|
|
79
|
my $self = shift; |
|
476
|
56
|
|
|
|
|
71
|
my $obj = shift; |
|
477
|
56
|
|
|
|
|
72
|
my $param_load_hash = shift; |
|
478
|
|
|
|
|
|
|
|
|
479
|
56
|
|
|
|
|
239
|
while( my($template_id, $rules) = each %$param_load_hash) { |
|
480
|
80
|
|
|
|
|
133
|
foreach my $rule_id ( keys %$rules ) { |
|
481
|
135
|
|
|
|
|
336
|
$obj->{'__load'}->{$template_id}->{$rule_id} = $rules->{$rule_id}; |
|
482
|
|
|
|
|
|
|
} |
|
483
|
|
|
|
|
|
|
} |
|
484
|
|
|
|
|
|
|
} |
|
485
|
|
|
|
|
|
|
|
|
486
|
|
|
|
|
|
|
|
|
487
|
|
|
|
|
|
|
# same as add_object_loading_info, but manipulates the data in $UR::Context::all_params_loaded |
|
488
|
|
|
|
|
|
|
sub _record_that_loading_has_occurred { |
|
489
|
147
|
|
|
147
|
|
174
|
my $self = shift; |
|
490
|
147
|
|
|
|
|
172
|
my $param_load_hash = shift; |
|
491
|
|
|
|
|
|
|
|
|
492
|
147
|
|
|
|
|
487
|
while( my($template_id, $rules) = each %$param_load_hash) { |
|
493
|
186
|
|
|
|
|
301
|
foreach my $rule_id ( keys %$rules ) { |
|
494
|
|
|
|
|
|
|
$UR::Context::all_params_loaded->{$template_id}->{$rule_id} ||= |
|
495
|
301
|
|
100
|
|
|
1309
|
$rules->{$rule_id}; |
|
496
|
|
|
|
|
|
|
} |
|
497
|
|
|
|
|
|
|
} |
|
498
|
|
|
|
|
|
|
} |
|
499
|
|
|
|
|
|
|
|
|
500
|
|
|
|
|
|
|
sub _first_class_in_inheritance_with_a_table { |
|
501
|
|
|
|
|
|
|
# This is called once per subclass and cached in the subclass from then on. |
|
502
|
78
|
|
|
78
|
|
106
|
my $self = shift; |
|
503
|
78
|
|
|
|
|
109
|
my $class = shift; |
|
504
|
78
|
50
|
|
|
|
191
|
$class = ref($class) if ref($class); |
|
505
|
|
|
|
|
|
|
|
|
506
|
|
|
|
|
|
|
|
|
507
|
78
|
50
|
|
|
|
179
|
unless ($class) { |
|
508
|
0
|
|
|
|
|
0
|
Carp::confess("No class?"); |
|
509
|
|
|
|
|
|
|
} |
|
510
|
78
|
|
|
|
|
283
|
my $class_object = $class->__meta__; |
|
511
|
78
|
|
|
|
|
132
|
my $found = ""; |
|
512
|
78
|
|
|
|
|
458
|
for ($class_object, $class_object->ancestry_class_metas) |
|
513
|
|
|
|
|
|
|
{ |
|
514
|
139
|
100
|
|
|
|
676
|
if ($_->has_direct_table) |
|
515
|
|
|
|
|
|
|
{ |
|
516
|
78
|
|
|
|
|
233
|
$found = $_->class_name; |
|
517
|
78
|
|
|
|
|
142
|
last; |
|
518
|
|
|
|
|
|
|
} |
|
519
|
|
|
|
|
|
|
} |
|
520
|
|
|
|
|
|
|
#eval qq/ |
|
521
|
|
|
|
|
|
|
# package $class; |
|
522
|
|
|
|
|
|
|
# sub _first_class_in_inheritance_with_a_table { |
|
523
|
|
|
|
|
|
|
# return '$found' if \$_[0] eq '$class'; |
|
524
|
|
|
|
|
|
|
# shift->SUPER::_first_class_in_inheritance_with_a_table(\@_); |
|
525
|
|
|
|
|
|
|
# } |
|
526
|
|
|
|
|
|
|
#/; |
|
527
|
|
|
|
|
|
|
#die "Error setting data in subclass: $@" if $@; |
|
528
|
78
|
|
|
|
|
210
|
return $found; |
|
529
|
|
|
|
|
|
|
} |
|
530
|
|
|
|
|
|
|
|
|
531
|
|
|
|
|
|
|
sub _class_is_safe_to_rebless_from_parent_class { |
|
532
|
78
|
|
|
78
|
|
155
|
my ($self, $class, $was_loaded_as_this_parent_class) = @_; |
|
533
|
78
|
|
|
|
|
391
|
my $fcwt = $self->_first_class_in_inheritance_with_a_table($class); |
|
534
|
78
|
50
|
|
|
|
206
|
unless ($fcwt) { |
|
535
|
0
|
|
|
|
|
0
|
Carp::croak("Can't call _class_is_safe_to_rebless_from_parent_class(): Class $class has no parent classes with a table"); |
|
536
|
|
|
|
|
|
|
} |
|
537
|
78
|
|
|
|
|
427
|
return ($was_loaded_as_this_parent_class->isa($fcwt)); |
|
538
|
|
|
|
|
|
|
} |
|
539
|
|
|
|
|
|
|
|
|
540
|
|
|
|
|
|
|
sub ur_datasource_class_for_dbi_connect_string { |
|
541
|
70
|
|
|
70
|
0
|
99
|
my($class, $dsn) = @_; |
|
542
|
70
|
|
|
|
|
398
|
my(undef, $driver) = DBI->parse_dsn($dsn); |
|
543
|
70
|
50
|
|
|
|
1379
|
$driver |
|
544
|
|
|
|
|
|
|
|| Carp::croak("Could not parse DBI driver out of connect string $dsn"); |
|
545
|
70
|
|
|
|
|
213
|
return 'UR::DataSource::'.$driver; |
|
546
|
|
|
|
|
|
|
} |
|
547
|
|
|
|
|
|
|
|
|
548
|
|
|
|
|
|
|
sub _get_current_entities { |
|
549
|
125
|
|
|
125
|
|
195
|
my $self = shift; |
|
550
|
125
|
|
|
|
|
587
|
my @class_meta = UR::Object::Type->is_loaded( |
|
551
|
|
|
|
|
|
|
data_source_id => $self->id |
|
552
|
|
|
|
|
|
|
); |
|
553
|
125
|
|
|
|
|
228
|
my @objects; |
|
554
|
125
|
|
|
|
|
256
|
for my $class_meta (@class_meta) { |
|
555
|
308
|
50
|
|
|
|
1112
|
next unless $class_meta->generated(); # Ungenerated classes won't have any instances |
|
556
|
308
|
|
|
|
|
558
|
my $class_name = $class_meta->class_name; |
|
557
|
308
|
|
|
|
|
570
|
push @objects, $UR::Context::current->all_objects_loaded($class_name); |
|
558
|
|
|
|
|
|
|
} |
|
559
|
125
|
|
|
|
|
1229
|
return @objects; |
|
560
|
|
|
|
|
|
|
} |
|
561
|
|
|
|
|
|
|
|
|
562
|
|
|
|
|
|
|
|
|
563
|
|
|
|
0
|
|
|
sub _prepare_for_lob { }; |
|
564
|
|
|
|
|
|
|
|
|
565
|
|
|
|
|
|
|
sub _set_specified_objects_saved_uncommitted { |
|
566
|
52
|
|
|
52
|
|
104
|
my ($self,$objects_arrayref) = @_; |
|
567
|
|
|
|
|
|
|
# Sets an objects as though the has been saved but tha changes have not been committed. |
|
568
|
|
|
|
|
|
|
# This is called automatically by _sync_databases. |
|
569
|
|
|
|
|
|
|
|
|
570
|
52
|
|
|
|
|
92
|
my %objects_by_class; |
|
571
|
|
|
|
|
|
|
my $class_name; |
|
572
|
52
|
|
|
|
|
139
|
for my $object (@$objects_arrayref) { |
|
573
|
134
|
|
|
|
|
216
|
$class_name = ref($object); |
|
574
|
134
|
|
100
|
|
|
475
|
$objects_by_class{$class_name} ||= []; |
|
575
|
134
|
|
|
|
|
122
|
push @{ $objects_by_class{$class_name} }, $object; |
|
|
134
|
|
|
|
|
255
|
|
|
576
|
|
|
|
|
|
|
} |
|
577
|
|
|
|
|
|
|
|
|
578
|
52
|
|
|
|
|
230
|
for my $class_name (sort keys %objects_by_class) { |
|
579
|
72
|
|
|
|
|
348
|
my $class_object = $class_name->__meta__; |
|
580
|
|
|
|
|
|
|
my @property_names = |
|
581
|
246
|
|
|
|
|
411
|
map { $_->property_name } |
|
582
|
72
|
|
|
|
|
574
|
grep { $_->column_name } |
|
|
371
|
|
|
|
|
624
|
|
|
583
|
|
|
|
|
|
|
$class_object->all_property_metas; |
|
584
|
|
|
|
|
|
|
|
|
585
|
72
|
|
|
|
|
142
|
for my $object (@{ $objects_by_class{$class_name} }) { |
|
|
72
|
|
|
|
|
189
|
|
|
586
|
134
|
|
100
|
|
|
562
|
$object->{db_saved_uncommitted} ||= {}; |
|
587
|
134
|
|
|
|
|
183
|
my $db_saved_uncommitted = $object->{db_saved_uncommitted}; |
|
588
|
134
|
|
|
|
|
167
|
for my $property ( @property_names ) { |
|
589
|
464
|
|
|
|
|
996
|
$db_saved_uncommitted->{$property} = $object->$property; |
|
590
|
|
|
|
|
|
|
} |
|
591
|
|
|
|
|
|
|
} |
|
592
|
|
|
|
|
|
|
} |
|
593
|
52
|
|
|
|
|
283
|
return 1; |
|
594
|
|
|
|
|
|
|
} |
|
595
|
|
|
|
|
|
|
|
|
596
|
|
|
|
|
|
|
sub _set_all_objects_saved_committed { |
|
597
|
|
|
|
|
|
|
# called by UR::DBI on commit |
|
598
|
103
|
|
|
103
|
|
174
|
my $self = shift; |
|
599
|
103
|
|
|
|
|
1204
|
return $self->_set_specified_objects_saved_committed([ $self->_get_current_entities ]); |
|
600
|
|
|
|
|
|
|
} |
|
601
|
|
|
|
|
|
|
|
|
602
|
|
|
|
|
|
|
sub _set_all_specified_objects_saved_committed { |
|
603
|
0
|
|
|
0
|
|
0
|
my $self = shift; |
|
604
|
0
|
|
|
|
|
0
|
my($pkg, $file, $line) = caller; |
|
605
|
0
|
|
|
|
|
0
|
Carp::carp("Deprecated method _set_all_specified_objects_saved_committed called at file $file line $line. The new name for this method is _set_specified_objects_saved_committed"); |
|
606
|
0
|
|
|
|
|
0
|
my @changed_objects = @_; |
|
607
|
0
|
|
|
|
|
0
|
$self->_set_specified_objects_saved_committed(\@changed_objects); |
|
608
|
|
|
|
|
|
|
} |
|
609
|
|
|
|
|
|
|
|
|
610
|
|
|
|
|
|
|
sub _set_specified_objects_saved_committed { |
|
611
|
108
|
|
|
108
|
|
186
|
my $self = shift; |
|
612
|
108
|
|
|
|
|
141
|
my $objects = shift; |
|
613
|
|
|
|
|
|
|
|
|
614
|
|
|
|
|
|
|
# Two step process... set saved and committed, then fire commit observers. |
|
615
|
|
|
|
|
|
|
# Doing so prevents problems should any of the observers themselves commit. |
|
616
|
108
|
|
|
|
|
135
|
my @saved_objects; |
|
617
|
108
|
|
|
|
|
208
|
for my $obj (@$objects) { |
|
618
|
677
|
|
|
|
|
900
|
my $saved = $self->_set_object_saved_committed($obj); |
|
619
|
677
|
100
|
|
|
|
948
|
push @saved_objects, $saved if $saved; |
|
620
|
|
|
|
|
|
|
} |
|
621
|
|
|
|
|
|
|
|
|
622
|
108
|
|
|
|
|
205
|
for my $obj (@saved_objects) { |
|
623
|
83
|
100
|
|
|
|
338
|
next if $obj->isa('UR::DeletedRef'); |
|
624
|
80
|
|
|
|
|
242
|
$obj->__signal_change__('commit'); |
|
625
|
80
|
100
|
|
|
|
312
|
if ($obj->isa('UR::Object::Ghost')) { |
|
626
|
20
|
|
|
|
|
67
|
$UR::Context::current->_abandon_object($obj); |
|
627
|
|
|
|
|
|
|
} |
|
628
|
|
|
|
|
|
|
} |
|
629
|
|
|
|
|
|
|
|
|
630
|
108
|
|
100
|
|
|
940
|
return scalar(@$objects) || "0 but true"; |
|
631
|
|
|
|
|
|
|
} |
|
632
|
|
|
|
|
|
|
|
|
633
|
|
|
|
|
|
|
sub _set_object_saved_committed { |
|
634
|
|
|
|
|
|
|
# called by the above, and some test cases |
|
635
|
677
|
|
|
677
|
|
506
|
my ($self, $object) = @_; |
|
636
|
677
|
100
|
|
|
|
851
|
if ($object->{db_saved_uncommitted}) { |
|
637
|
83
|
100
|
|
|
|
394
|
unless ($object->isa('UR::Object::Ghost')) { |
|
638
|
60
|
|
|
|
|
1928
|
%{ $object->{db_committed} } = ( |
|
639
|
24
|
|
|
|
|
92
|
($object->{db_committed} ? %{ $object->{db_committed} } : ()), |
|
640
|
60
|
100
|
|
|
|
134
|
%{ $object->{db_saved_uncommitted} } |
|
|
60
|
|
|
|
|
153
|
|
|
641
|
|
|
|
|
|
|
); |
|
642
|
60
|
|
|
|
|
166
|
delete $object->{db_saved_uncommitted}; |
|
643
|
|
|
|
|
|
|
} |
|
644
|
83
|
|
|
|
|
119
|
return $object; |
|
645
|
|
|
|
|
|
|
} |
|
646
|
|
|
|
|
|
|
else { |
|
647
|
594
|
|
|
|
|
482
|
return; |
|
648
|
|
|
|
|
|
|
} |
|
649
|
|
|
|
|
|
|
} |
|
650
|
|
|
|
|
|
|
|
|
651
|
|
|
|
|
|
|
sub _set_all_objects_saved_rolled_back { |
|
652
|
|
|
|
|
|
|
# called by UR::DBI on commit |
|
653
|
22
|
|
|
22
|
|
26
|
my $self = shift; |
|
654
|
22
|
|
|
|
|
74
|
my @objects = $self->_get_current_entities; |
|
655
|
22
|
|
|
|
|
55
|
for my $obj (@objects) { |
|
656
|
203
|
50
|
|
|
|
226
|
unless ($self->_set_object_saved_rolled_back($obj)) { |
|
657
|
0
|
|
|
|
|
0
|
die "An error occurred setting " . $obj->__display_name__ |
|
658
|
|
|
|
|
|
|
. " to match the rolled-back database state. Exiting..."; |
|
659
|
|
|
|
|
|
|
} |
|
660
|
|
|
|
|
|
|
} |
|
661
|
|
|
|
|
|
|
} |
|
662
|
|
|
|
|
|
|
|
|
663
|
|
|
|
|
|
|
sub _set_specified_objects_saved_rolled_back { |
|
664
|
0
|
|
|
0
|
|
0
|
my $self = shift; |
|
665
|
0
|
|
|
|
|
0
|
my $objects = shift; |
|
666
|
0
|
|
|
|
|
0
|
for my $obj (@$objects) { |
|
667
|
0
|
0
|
|
|
|
0
|
unless ($self->_set_object_saved_rolled_back($obj)) { |
|
668
|
0
|
|
|
|
|
0
|
die "An error occurred setting " . $obj->__display_name__ |
|
669
|
|
|
|
|
|
|
. " to match the rolled-back database state. Exiting..."; |
|
670
|
|
|
|
|
|
|
} |
|
671
|
|
|
|
|
|
|
} |
|
672
|
|
|
|
|
|
|
} |
|
673
|
|
|
|
|
|
|
|
|
674
|
|
|
|
|
|
|
|
|
675
|
|
|
|
|
|
|
|
|
676
|
|
|
|
|
|
|
sub _set_object_saved_rolled_back { |
|
677
|
|
|
|
|
|
|
# called by the above, and some test cases |
|
678
|
203
|
|
|
203
|
|
127
|
my ($self,$object) = @_; |
|
679
|
203
|
|
|
|
|
156
|
delete $object->{db_saved_uncommitted}; |
|
680
|
203
|
|
|
|
|
335
|
return $object; |
|
681
|
|
|
|
|
|
|
} |
|
682
|
|
|
|
|
|
|
|
|
683
|
|
|
|
|
|
|
|
|
684
|
|
|
|
|
|
|
# These are part of the basic DataSource API. Subclasses will want to override these |
|
685
|
|
|
|
|
|
|
|
|
686
|
|
|
|
|
|
|
sub _sync_database { |
|
687
|
0
|
|
|
0
|
|
0
|
my $class = shift; |
|
688
|
0
|
|
|
|
|
0
|
my %args = @_; |
|
689
|
0
|
|
0
|
|
|
0
|
$class = ref($class) || $class; |
|
690
|
|
|
|
|
|
|
|
|
691
|
|
|
|
|
|
|
$class->warning_message("Data source $class does not support saving objects to storage. " . |
|
692
|
0
|
|
|
|
|
0
|
scalar(@{$args{'changed_objects'}}) . " objects will not be saved"); |
|
|
0
|
|
|
|
|
0
|
|
|
693
|
0
|
|
|
|
|
0
|
return 1; |
|
694
|
|
|
|
|
|
|
} |
|
695
|
|
|
|
|
|
|
|
|
696
|
|
|
|
|
|
|
sub commit { |
|
697
|
5
|
|
|
5
|
1
|
6
|
my $class = shift; |
|
698
|
5
|
|
|
|
|
6
|
my %args = @_; |
|
699
|
5
|
|
33
|
|
|
14
|
$class = ref($class) || $class; |
|
700
|
|
|
|
|
|
|
|
|
701
|
|
|
|
|
|
|
#$class->warning_message("commit() ignored for data source $class"); |
|
702
|
5
|
|
|
|
|
9
|
return 1; |
|
703
|
|
|
|
|
|
|
} |
|
704
|
|
|
|
|
|
|
|
|
705
|
|
|
|
|
|
|
sub rollback { |
|
706
|
0
|
|
|
0
|
1
|
0
|
my $class = shift; |
|
707
|
0
|
|
|
|
|
0
|
my %args = @_; |
|
708
|
0
|
|
0
|
|
|
0
|
$class = ref($class) || $class; |
|
709
|
|
|
|
|
|
|
|
|
710
|
0
|
|
|
|
|
0
|
$class->warning_message("rollback() ignored for data source $class"); |
|
711
|
0
|
|
|
|
|
0
|
return 1; |
|
712
|
|
|
|
|
|
|
} |
|
713
|
|
|
|
|
|
|
|
|
714
|
|
|
|
|
|
|
# When the class initializer is create property objects, it will |
|
715
|
|
|
|
|
|
|
# auto-fill-in column_name if the class definition has a table_name. |
|
716
|
|
|
|
|
|
|
# File-based data sources do not have tables (and so classes using them |
|
717
|
|
|
|
|
|
|
# do not have table_names), but the properties still need column_names |
|
718
|
|
|
|
|
|
|
# so loading works properly. |
|
719
|
|
|
|
|
|
|
# For now, only UR::DataSource::File and ::FileMux set this. |
|
720
|
|
|
|
|
|
|
# FIXME this method's existence is ugly. Find a better way to fill in |
|
721
|
|
|
|
|
|
|
# column_name for those properties, or fix the data sources to not |
|
722
|
|
|
|
|
|
|
# require column_names to be set by the initializer |
|
723
|
|
|
|
|
|
|
sub initializer_should_create_column_name_for_class_properties { |
|
724
|
2053
|
|
|
2053
|
0
|
8438
|
return 0; |
|
725
|
|
|
|
|
|
|
} |
|
726
|
|
|
|
|
|
|
|
|
727
|
|
|
|
|
|
|
|
|
728
|
|
|
|
|
|
|
# Subclasses should override this. |
|
729
|
|
|
|
|
|
|
# It's called by the class initializer when the data_source property in a class |
|
730
|
|
|
|
|
|
|
# definition contains a hashref with an 'is' key. The method should accept this |
|
731
|
|
|
|
|
|
|
# hashref, create a data_source instance (if appropriate) and return the class_name |
|
732
|
|
|
|
|
|
|
# of this new datasource. |
|
733
|
|
|
|
|
|
|
sub create_from_inline_class_data { |
|
734
|
0
|
|
|
0
|
1
|
0
|
my ($class,$class_data,$ds_data) = @_; |
|
735
|
0
|
|
|
|
|
0
|
my %ds_data = %$ds_data; |
|
736
|
0
|
|
|
|
|
0
|
my $ds_class_name = delete $ds_data{is}; |
|
737
|
0
|
0
|
|
|
|
0
|
unless (my $ds_class_meta = UR::Object::Type->get($ds_class_name)) { |
|
738
|
0
|
|
|
|
|
0
|
die "No class $ds_class_name found!"; |
|
739
|
|
|
|
|
|
|
} |
|
740
|
0
|
|
|
|
|
0
|
my $ds = $ds_class_name->__define__(%ds_data); |
|
741
|
0
|
0
|
|
|
|
0
|
unless ($ds) { |
|
742
|
0
|
|
|
|
|
0
|
die "Failed to construct $ds_class_name: " . $ds_class_name->error_message(); |
|
743
|
|
|
|
|
|
|
} |
|
744
|
0
|
|
|
|
|
0
|
return $ds; |
|
745
|
|
|
|
|
|
|
} |
|
746
|
|
|
|
|
|
|
|
|
747
|
|
|
|
|
|
|
sub ur_data_type_for_data_source_data_type { |
|
748
|
0
|
|
|
0
|
0
|
0
|
my($class,$type) = @_; |
|
749
|
|
|
|
|
|
|
|
|
750
|
0
|
|
|
|
|
0
|
return [undef,undef]; # The default that should give reasonable behavior |
|
751
|
|
|
|
|
|
|
} |
|
752
|
|
|
|
|
|
|
|
|
753
|
|
|
|
|
|
|
|
|
754
|
|
|
|
|
|
|
# prepare_for_fork, do_after_fork_in_child, and finish_up_after_fork are no-op |
|
755
|
|
|
|
|
|
|
# here in the UR::DataSource base class and should be implented in subclasses |
|
756
|
|
|
|
|
|
|
# as needed. |
|
757
|
2
|
|
|
2
|
0
|
6
|
sub prepare_for_fork { return 1 } |
|
758
|
2
|
|
|
2
|
0
|
14
|
sub do_after_fork_in_child { return 1 } |
|
759
|
2
|
|
|
2
|
0
|
20
|
sub finish_up_after_fork { return 1 } |
|
760
|
|
|
|
|
|
|
|
|
761
|
|
|
|
|
|
|
sub _resolve_owner_and_table_from_table_name { |
|
762
|
546
|
|
|
546
|
|
486
|
my($self, $table_name) = @_; |
|
763
|
|
|
|
|
|
|
# Basic data sources don't know about owners/schemas |
|
764
|
546
|
|
|
|
|
893
|
return (undef, $table_name); |
|
765
|
|
|
|
|
|
|
} |
|
766
|
|
|
|
|
|
|
|
|
767
|
|
|
|
|
|
|
sub _resolve_table_and_column_from_column_name { |
|
768
|
546
|
|
|
546
|
|
712
|
my($self, $column_name) = @_; |
|
769
|
|
|
|
|
|
|
# Basic data sources don't know about tables |
|
770
|
546
|
|
|
|
|
1077
|
return (undef,$column_name); |
|
771
|
|
|
|
|
|
|
} |
|
772
|
|
|
|
|
|
|
|
|
773
|
|
|
|
|
|
|
1; |