File Coverage

lib/UR/DataSource.pm
Criterion Covered Total %
statement 249 318 78.3
branch 40 64 62.5
condition 22 34 64.7
subroutine 44 63 69.8
pod 8 30 26.6
total 363 509 71.3


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;