line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package DBIx::MoCo; |
2
|
15
|
|
|
15
|
|
792121
|
use strict; |
|
15
|
|
|
|
|
38
|
|
|
15
|
|
|
|
|
579
|
|
3
|
15
|
|
|
15
|
|
83
|
use warnings; |
|
15
|
|
|
|
|
45
|
|
|
15
|
|
|
|
|
414
|
|
4
|
15
|
|
|
15
|
|
77
|
use base qw (Class::Data::Inheritable); |
|
15
|
|
|
|
|
31
|
|
|
15
|
|
|
|
|
13460
|
|
5
|
|
|
|
|
|
|
|
6
|
15
|
|
|
15
|
|
15524
|
use DBIx::MoCo::Relation; |
|
15
|
|
|
|
|
42
|
|
|
15
|
|
|
|
|
175
|
|
7
|
15
|
|
|
15
|
|
9428
|
use DBIx::MoCo::List; |
|
15
|
|
|
|
|
88
|
|
|
15
|
|
|
|
|
274
|
|
8
|
15
|
|
|
15
|
|
10333
|
use DBIx::MoCo::Cache; |
|
15
|
|
|
|
|
44
|
|
|
15
|
|
|
|
|
196
|
|
9
|
15
|
|
|
15
|
|
9131
|
use DBIx::MoCo::Cache::Dummy; |
|
15
|
|
|
|
|
208
|
|
|
15
|
|
|
|
|
156
|
|
10
|
15
|
|
|
15
|
|
8617
|
use DBIx::MoCo::Schema; |
|
15
|
|
|
|
|
46
|
|
|
15
|
|
|
|
|
150
|
|
11
|
15
|
|
|
15
|
|
7732
|
use DBIx::MoCo::Column; |
|
15
|
|
|
|
|
42
|
|
|
15
|
|
|
|
|
167
|
|
12
|
|
|
|
|
|
|
|
13
|
15
|
|
|
15
|
|
347
|
use Carp; |
|
15
|
|
|
|
|
29
|
|
|
15
|
|
|
|
|
732
|
|
14
|
15
|
|
|
15
|
|
12284
|
use Class::Trigger; |
|
15
|
|
|
|
|
21843
|
|
|
15
|
|
|
|
|
102
|
|
15
|
15
|
|
|
15
|
|
21047
|
use Tie::IxHash; |
|
15
|
|
|
|
|
88563
|
|
|
15
|
|
|
|
|
187
|
|
16
|
15
|
|
|
15
|
|
520
|
use File::Spec; |
|
15
|
|
|
|
|
37
|
|
|
15
|
|
|
|
|
173
|
|
17
|
15
|
|
|
15
|
|
359
|
use UNIVERSAL::require; |
|
15
|
|
|
|
|
53
|
|
|
15
|
|
|
|
|
120
|
|
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
our $VERSION = '0.18'; |
20
|
|
|
|
|
|
|
our $AUTOLOAD; |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
my $cache_status = { |
23
|
|
|
|
|
|
|
retrieve_count => 0, |
24
|
|
|
|
|
|
|
retrieve_cache_count => 0, |
25
|
|
|
|
|
|
|
retrieve_icache_count => 0, |
26
|
|
|
|
|
|
|
retrieve_all_count => 0, |
27
|
|
|
|
|
|
|
has_many_count => 0, |
28
|
|
|
|
|
|
|
has_many_cache_count => 0, |
29
|
|
|
|
|
|
|
has_many_icache_count => 0, |
30
|
|
|
|
|
|
|
retrieved_oids => [], |
31
|
|
|
|
|
|
|
}; |
32
|
|
|
|
|
|
|
my ($db,$session,$schema); |
33
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
__PACKAGE__->mk_classdata($_) for |
35
|
|
|
|
|
|
|
qw(cache_object default_cache_expiration icache_expiration |
36
|
|
|
|
|
|
|
cache_null_object table cache_cols_only _db_object save_explicitly list_class); |
37
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
## NOTE: INIT block does not work well under mod_perl or POE. |
39
|
|
|
|
|
|
|
## Please set cache_object() explicitly if you want to use transparent caching. |
40
|
|
|
|
|
|
|
# INIT { |
41
|
|
|
|
|
|
|
# unless (defined __PACKAGE__->cache_object) { |
42
|
|
|
|
|
|
|
# if (Cache::FastMmap->require) { |
43
|
|
|
|
|
|
|
# my $file = File::Spec->catfile('/tmp', __PACKAGE__); |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
# File::Spec->require or die $@; |
46
|
|
|
|
|
|
|
# __PACKAGE__->cache_object( |
47
|
|
|
|
|
|
|
# Cache::FastMmap->new( |
48
|
|
|
|
|
|
|
# share_file => $file, |
49
|
|
|
|
|
|
|
# unlink_on_exit => 1, |
50
|
|
|
|
|
|
|
# expire_time => 600, # sec |
51
|
|
|
|
|
|
|
# ) or die $! |
52
|
|
|
|
|
|
|
# ); |
53
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
# chmod(0666, $file) or die $! if -e $file; |
55
|
|
|
|
|
|
|
# } else { |
56
|
|
|
|
|
|
|
# warn "Using DBIx::MoCo::Cache is now deprecated because of memory leak." |
57
|
|
|
|
|
|
|
# . "Install Cache::FastMmap instead, or setup cache_object explicitly."; |
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
# DBIx::MoCo::Cache->require or die $@; |
60
|
|
|
|
|
|
|
# __PACKAGE__->cache_object( DBIx::MoCo::Cache->new ); |
61
|
|
|
|
|
|
|
# } |
62
|
|
|
|
|
|
|
# } |
63
|
|
|
|
|
|
|
# } |
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
__PACKAGE__->default_cache_expiration(60 * 60 * 3); # 3 hours |
66
|
|
|
|
|
|
|
__PACKAGE__->icache_expiration(0); # Instance cache |
67
|
|
|
|
|
|
|
__PACKAGE__->cache_null_object(1); |
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
# SESSION & CACHE CONTROLLERS |
70
|
|
|
|
|
|
|
__PACKAGE__->add_trigger(after_create => sub { |
71
|
|
|
|
|
|
|
my ($class, $self) = @_; |
72
|
|
|
|
|
|
|
$self or confess '$self is not specified'; |
73
|
|
|
|
|
|
|
$class->store_self_cache($self); |
74
|
|
|
|
|
|
|
$class->flush_belongs_to($self); |
75
|
|
|
|
|
|
|
}); |
76
|
|
|
|
|
|
|
__PACKAGE__->add_trigger(before_update => sub { |
77
|
|
|
|
|
|
|
my ($class, $self) = @_; |
78
|
|
|
|
|
|
|
$self or confess '$self is not specified'; |
79
|
|
|
|
|
|
|
$class->flush_self_cache($self); |
80
|
|
|
|
|
|
|
}); |
81
|
|
|
|
|
|
|
__PACKAGE__->add_trigger(after_update => sub { |
82
|
|
|
|
|
|
|
my ($class, $self) = @_; |
83
|
|
|
|
|
|
|
$self or confess '$self is not specified'; |
84
|
|
|
|
|
|
|
$class->store_self_cache($self); |
85
|
|
|
|
|
|
|
}); |
86
|
|
|
|
|
|
|
__PACKAGE__->add_trigger(before_delete => sub { |
87
|
|
|
|
|
|
|
my ($class, $self) = @_; |
88
|
|
|
|
|
|
|
$self or confess '$self is not specified'; |
89
|
|
|
|
|
|
|
$class->flush_self_cache($self); |
90
|
|
|
|
|
|
|
$class->flush_belongs_to($self); |
91
|
|
|
|
|
|
|
}); |
92
|
|
|
|
|
|
|
|
93
|
139
|
|
|
139
|
1
|
1897
|
sub cache_status { $cache_status } |
94
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
sub cache { |
96
|
385
|
|
|
385
|
0
|
935
|
my $class = shift; |
97
|
385
|
100
|
|
|
|
1324
|
$class = ref($class) if ref($class); |
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
## It is no matter costs of creating Dummy objects because it is a singleton. |
100
|
385
|
|
66
|
|
|
1566
|
my $cache = $class->cache_object || DBIx::MoCo::Cache::Dummy->instance; |
101
|
|
|
|
|
|
|
|
102
|
385
|
|
|
|
|
6787
|
my ($k,$v,$ex) = @_; |
103
|
|
|
|
|
|
|
# warn "$cache in $class"; |
104
|
385
|
|
|
|
|
1344
|
my $s = $class->is_in_session; |
105
|
385
|
100
|
|
|
|
1474
|
if (defined $v) { |
|
|
50
|
|
|
|
|
|
106
|
200
|
|
33
|
|
|
1953
|
$ex ||= $class->default_cache_expiration; |
107
|
200
|
50
|
33
|
|
|
3651
|
$ex = "+$ex" if ($ex && ref($cache) eq 'Cache::Memory'); |
108
|
200
|
100
|
|
|
|
821
|
if ($v eq '') { |
109
|
28
|
50
|
|
|
|
190
|
if ($cache->can('remove')) { |
110
|
28
|
|
|
|
|
112
|
$cache->remove($k); |
111
|
|
|
|
|
|
|
} |
112
|
28
|
100
|
|
|
|
117
|
if ($s) { |
113
|
1
|
50
|
|
|
|
5
|
delete $s->{cache}->{$k} if $k; |
114
|
|
|
|
|
|
|
} |
115
|
|
|
|
|
|
|
} else { |
116
|
172
|
100
|
66
|
|
|
975
|
if ($class->cache_cols_only && ref($v) && |
|
|
|
100
|
|
|
|
|
|
|
|
66
|
|
|
|
|
117
|
|
|
|
|
|
|
ref($v) =~ /::/ && $v->isa($class)) { |
118
|
|
|
|
|
|
|
# remove additional elements |
119
|
42
|
|
|
|
|
1137
|
my @cols = @{$v->columns}; |
|
42
|
|
|
|
|
164
|
|
120
|
42
|
|
|
|
|
118
|
for (qw(changed_cols to_be_updated object_id)) { |
121
|
126
|
100
|
|
|
|
588
|
push @cols, $_ if (defined $v->{$_}); |
122
|
|
|
|
|
|
|
} |
123
|
42
|
|
|
|
|
315
|
my $hash = {map {$_ => $v->{$_}} @cols}; |
|
168
|
|
|
|
|
673
|
|
124
|
42
|
|
|
|
|
243
|
my $o = bless $hash, $class; |
125
|
42
|
|
|
|
|
278
|
$cache->set($k,$o,$ex); |
126
|
42
|
50
|
|
|
|
246
|
$s->{cache}->{$k} = $o if $s; |
127
|
|
|
|
|
|
|
} else { |
128
|
130
|
|
|
|
|
1927
|
$cache->set($k,$v,$ex); |
129
|
130
|
100
|
|
|
|
509
|
$s->{cache}->{$k} = $v if $s; |
130
|
|
|
|
|
|
|
} |
131
|
|
|
|
|
|
|
} |
132
|
|
|
|
|
|
|
# warn $cache . '->set(' . $k . ')'; |
133
|
200
|
|
|
|
|
888
|
return $v; |
134
|
|
|
|
|
|
|
} elsif ($k) { |
135
|
|
|
|
|
|
|
# warn "hit session cache for $k" if ($s && $s->{cache}->{$k}); |
136
|
185
|
|
100
|
|
|
1697
|
return $s->{cache}->{$k} || $cache->get($k); |
137
|
|
|
|
|
|
|
} |
138
|
|
|
|
|
|
|
} |
139
|
|
|
|
|
|
|
|
140
|
0
|
|
|
0
|
1
|
0
|
sub flush_belongs_to {} # it's delivered from MoCo::Relation |
141
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
sub flush_self_cache { |
143
|
26
|
|
|
26
|
1
|
60
|
my ($class, $self) = @_; |
144
|
26
|
50
|
33
|
|
|
128
|
if (!$self && ref $class) { |
145
|
0
|
|
|
|
|
0
|
$self = $class; |
146
|
0
|
|
|
|
|
0
|
$class = ref $self; |
147
|
|
|
|
|
|
|
} |
148
|
26
|
50
|
|
|
|
98
|
$self or confess '$self is not specified'; |
149
|
|
|
|
|
|
|
|
150
|
26
|
100
|
|
|
|
277
|
return unless $class->cache_object; |
151
|
|
|
|
|
|
|
|
152
|
10
|
50
|
|
|
|
121
|
my $rm = $class->cache_object->can('remove') ? 'remove' : 'delete'; |
153
|
10
|
|
|
|
|
130
|
for (@{$self->object_ids}) { |
|
10
|
|
|
|
|
31
|
|
154
|
|
|
|
|
|
|
# warn "flush $_"; |
155
|
|
|
|
|
|
|
#weaken($class->cache($_)); |
156
|
18
|
|
|
|
|
65
|
$class->cache_object->$rm($_); |
157
|
|
|
|
|
|
|
} |
158
|
|
|
|
|
|
|
} |
159
|
|
|
|
|
|
|
|
160
|
|
|
|
|
|
|
sub store_self_cache { |
161
|
86
|
|
|
86
|
1
|
264
|
my ($class, $self) = @_; |
162
|
86
|
100
|
66
|
|
|
547
|
if (!$self && ref $class) { |
163
|
3
|
|
|
|
|
8
|
$self = $class; |
164
|
3
|
|
|
|
|
9
|
$class = ref $self; |
165
|
|
|
|
|
|
|
} |
166
|
86
|
50
|
|
|
|
583
|
$self or confess '$self is not specified'; |
167
|
|
|
|
|
|
|
# warn "store $_" for @{$self->object_ids}; |
168
|
86
|
|
|
|
|
552
|
my $icache = $self->icache; |
169
|
86
|
|
|
|
|
620
|
$self->flush_icache; |
170
|
86
|
|
|
|
|
147
|
$class->cache($_, $self) for @{$self->object_ids}; |
|
86
|
|
|
|
|
616
|
|
171
|
86
|
100
|
|
|
|
555
|
$self->icache($icache) if $icache; |
172
|
|
|
|
|
|
|
} |
173
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
sub icache { |
175
|
125
|
|
|
125
|
0
|
422
|
my $self = shift; |
176
|
125
|
100
|
|
|
|
420
|
if ($_[0]) { |
177
|
1
|
|
|
|
|
4
|
$self->{_icache} = shift; |
178
|
|
|
|
|
|
|
} else { |
179
|
124
|
|
|
|
|
786
|
my $ex = $self->icache_expiration; |
180
|
124
|
100
|
|
|
|
1576
|
$ex > 0 or return; |
181
|
17
|
100
|
100
|
|
|
161
|
if (!$self->{_icache} || |
182
|
|
|
|
|
|
|
($self->{_icache}->{_created} + $ex < time())) { |
183
|
5
|
|
|
|
|
58
|
$self->{_icache} = {_created => time()}; |
184
|
|
|
|
|
|
|
} |
185
|
|
|
|
|
|
|
} |
186
|
18
|
|
|
|
|
75
|
return $self->{_icache}; |
187
|
|
|
|
|
|
|
} |
188
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
sub flush_icache { |
190
|
96
|
|
|
96
|
0
|
219
|
my $self = shift; |
191
|
96
|
100
|
|
|
|
1409
|
$self->{_icache} or return; |
192
|
2
|
50
|
|
|
|
8
|
if ($_[0]) { |
193
|
|
|
|
|
|
|
# warn "flush icache $_[0] for " . $self; |
194
|
0
|
|
|
|
|
0
|
delete $self->{_icache}->{$_[0]}; |
195
|
|
|
|
|
|
|
} else { |
196
|
2
|
|
|
|
|
6
|
$self->{_icache} = undef; |
197
|
|
|
|
|
|
|
} |
198
|
|
|
|
|
|
|
} |
199
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
sub has_many_keys_cache_name { |
201
|
46
|
|
|
46
|
0
|
528
|
my $self = shift; |
202
|
46
|
50
|
|
|
|
144
|
my $attr = shift or return; |
203
|
46
|
50
|
|
|
|
131
|
my $oid = $self->object_id or return; |
204
|
46
|
|
|
|
|
323
|
return sprintf('%s-%s_keys', $oid, $attr); |
205
|
|
|
|
|
|
|
} |
206
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
sub flush_has_many_keys { |
208
|
11
|
|
|
11
|
0
|
40
|
my $self = shift; |
209
|
11
|
50
|
|
|
|
51
|
my $attr = shift or return; |
210
|
|
|
|
|
|
|
# $self->flush($self->has_many_keys_name($attr)); |
211
|
|
|
|
|
|
|
# $self->flush($self->has_many_max_offset_name($attr)); |
212
|
11
|
|
|
|
|
55
|
my $key = $self->has_many_keys_cache_name($attr); |
213
|
11
|
|
|
|
|
36
|
$self->cache($key, ''); |
214
|
|
|
|
|
|
|
} |
215
|
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
# session controllers |
217
|
|
|
|
|
|
|
sub start_session { |
218
|
7
|
|
|
7
|
1
|
1934
|
my $class = shift; |
219
|
7
|
100
|
|
|
|
96
|
$class->end_session if $class->is_in_session; |
220
|
7
|
|
|
|
|
65
|
$session = { |
221
|
|
|
|
|
|
|
changed_objects => [], |
222
|
|
|
|
|
|
|
cache => {}, |
223
|
|
|
|
|
|
|
pid => $$, |
224
|
|
|
|
|
|
|
created => time(), |
225
|
|
|
|
|
|
|
}; |
226
|
|
|
|
|
|
|
} |
227
|
|
|
|
|
|
|
|
228
|
560
|
|
|
560
|
1
|
3579
|
sub is_in_session { $session } |
229
|
15
|
|
|
15
|
0
|
368
|
sub session { $session } |
230
|
|
|
|
|
|
|
sub session_cache { |
231
|
0
|
0
|
|
0
|
0
|
0
|
my $s = shift->session or return; |
232
|
0
|
|
|
|
|
0
|
return $s->{cache}; |
233
|
|
|
|
|
|
|
} |
234
|
|
|
|
|
|
|
|
235
|
|
|
|
|
|
|
sub end_session { |
236
|
6
|
|
|
6
|
1
|
876
|
my $class = shift; |
237
|
6
|
50
|
|
|
|
23
|
$session or return; |
238
|
6
|
|
|
|
|
19
|
$class->save_changed; |
239
|
6
|
|
|
|
|
20
|
$cache_status->{retrieved_oids} = []; |
240
|
6
|
|
|
|
|
34
|
$session = undef; |
241
|
|
|
|
|
|
|
} |
242
|
|
|
|
|
|
|
|
243
|
|
|
|
|
|
|
sub save_changed { |
244
|
113
|
|
|
113
|
0
|
207
|
my $class = shift; |
245
|
113
|
100
|
|
|
|
452
|
$class->is_in_session or return; |
246
|
11
|
|
|
|
|
19
|
for (@{$class->session->{changed_objects}}) { |
|
11
|
|
|
|
|
29
|
|
247
|
4
|
50
|
|
|
|
11
|
$_ or next; |
248
|
4
|
|
|
|
|
23
|
$_->save; |
249
|
|
|
|
|
|
|
} |
250
|
|
|
|
|
|
|
} |
251
|
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
# CLASS DEFINISION METHODS |
253
|
96
|
|
|
96
|
0
|
574
|
sub relation { 'DBIx::MoCo::Relation' } |
254
|
|
|
|
|
|
|
|
255
|
|
|
|
|
|
|
sub db_object { |
256
|
212
|
|
|
212
|
0
|
6470
|
my $class = shift; |
257
|
212
|
100
|
|
|
|
790
|
if (my $db = shift) { |
258
|
19
|
|
|
|
|
201
|
$class->_db_object($db); |
259
|
|
|
|
|
|
|
} |
260
|
212
|
|
|
|
|
2152
|
$class->_db_object; |
261
|
|
|
|
|
|
|
} |
262
|
|
|
|
|
|
|
|
263
|
|
|
|
|
|
|
sub has_a { |
264
|
48
|
|
|
48
|
1
|
37357
|
my $class = shift; |
265
|
48
|
|
|
|
|
315
|
$class->relation->register($class, 'has_a', @_); |
266
|
|
|
|
|
|
|
} |
267
|
|
|
|
|
|
|
sub has_many { |
268
|
48
|
|
|
48
|
1
|
5718
|
my $class = shift; |
269
|
48
|
|
|
|
|
225
|
$class->relation->register($class, 'has_many', @_); |
270
|
|
|
|
|
|
|
} |
271
|
|
|
|
|
|
|
|
272
|
|
|
|
|
|
|
sub schema { |
273
|
631
|
|
|
631
|
1
|
13768
|
my $class = shift; |
274
|
631
|
100
|
|
|
|
1873
|
$class = ref $class if ref $class; |
275
|
631
|
100
|
|
|
|
1932
|
unless ($schema->{$class}) { |
276
|
20
|
|
|
|
|
189
|
$schema->{$class} = DBIx::MoCo::Schema->new($class); |
277
|
|
|
|
|
|
|
} |
278
|
631
|
|
|
|
|
3557
|
return $schema->{$class}; |
279
|
|
|
|
|
|
|
} |
280
|
|
|
|
|
|
|
|
281
|
|
|
|
|
|
|
for my $attr (qw/primary_keys unique_keys retrieve_keys columns/) { |
282
|
|
|
|
|
|
|
my $classdata = "_" . $attr; |
283
|
|
|
|
|
|
|
__PACKAGE__->mk_classdata($classdata); |
284
|
|
|
|
|
|
|
|
285
|
15
|
|
|
15
|
|
36525
|
no strict 'refs'; |
|
15
|
|
|
|
|
44
|
|
|
15
|
|
|
|
|
52504
|
|
286
|
|
|
|
|
|
|
*{__PACKAGE__ . "\::$attr"} = sub { |
287
|
626
|
|
|
626
|
|
5375
|
my $class = shift; |
288
|
626
|
100
|
|
|
|
1800
|
if (@_) { |
289
|
1
|
50
|
33
|
|
|
13
|
my @keys = (ref $_[0] and ref $_[0] eq 'ARRAY') ? @{$_[0]} : @_; |
|
1
|
|
|
|
|
4
|
|
290
|
1
|
|
|
|
|
6
|
$class->$classdata(\@keys); |
291
|
|
|
|
|
|
|
} else { |
292
|
625
|
100
|
|
|
|
3117
|
$class->$classdata |
293
|
|
|
|
|
|
|
? $class->$classdata |
294
|
|
|
|
|
|
|
: $class->schema->$attr; |
295
|
|
|
|
|
|
|
} |
296
|
|
|
|
|
|
|
}; |
297
|
|
|
|
|
|
|
} |
298
|
|
|
|
|
|
|
|
299
|
|
|
|
|
|
|
sub has_muid { |
300
|
66
|
|
|
66
|
0
|
125
|
my $class = shift; |
301
|
|
|
|
|
|
|
return ($class->has_column('muid') && |
302
|
66
|
|
33
|
|
|
355
|
scalar @{$class->primary_keys} == 1); |
303
|
|
|
|
|
|
|
} |
304
|
|
|
|
|
|
|
|
305
|
|
|
|
|
|
|
sub has_column { |
306
|
71
|
|
|
71
|
1
|
145
|
my $class = shift; |
307
|
71
|
50
|
|
|
|
225
|
my $col = shift or return; |
308
|
71
|
100
|
|
|
|
553
|
$class->columns or return; |
309
|
70
|
|
|
|
|
157
|
grep { $col eq $_ } @{$class->columns}; |
|
224
|
|
|
|
|
938
|
|
|
70
|
|
|
|
|
244
|
|
310
|
|
|
|
|
|
|
} |
311
|
|
|
|
|
|
|
|
312
|
|
|
|
|
|
|
sub utf8_columns { |
313
|
5
|
|
|
5
|
1
|
1515
|
my $class = shift; |
314
|
5
|
|
|
|
|
29
|
$class->schema->utf8_columns(@_); |
315
|
|
|
|
|
|
|
} |
316
|
|
|
|
|
|
|
|
317
|
|
|
|
|
|
|
sub is_utf8_column { |
318
|
3
|
|
|
3
|
0
|
269
|
my $class = shift; |
319
|
3
|
50
|
|
|
|
15
|
my $col = shift or return; |
320
|
3
|
50
|
|
|
|
25
|
my $utf8 = $class->utf8_columns or return; |
321
|
3
|
50
|
|
|
|
16
|
ref $utf8 eq 'ARRAY' or return; |
322
|
3
|
|
|
|
|
7
|
return grep { $_ eq $col } @$utf8; |
|
6
|
|
|
|
|
30
|
|
323
|
|
|
|
|
|
|
} |
324
|
|
|
|
|
|
|
|
325
|
|
|
|
|
|
|
# DATA OPERATIONAL METHODS |
326
|
|
|
|
|
|
|
sub object_id { |
327
|
658
|
|
|
658
|
0
|
1814
|
my $self = shift; |
328
|
658
|
|
66
|
|
|
2835
|
my $class = ref($self) || $self; |
329
|
658
|
100
|
|
|
|
1951
|
$self = undef unless ref($self); |
330
|
658
|
100
|
100
|
|
|
3129
|
if ($self && $self->{object_id}) { |
331
|
226
|
|
|
|
|
1239
|
return $self->{object_id}; |
332
|
|
|
|
|
|
|
} |
333
|
432
|
|
50
|
|
|
1786
|
my $prefix = $class->object_id_prefix || ''; |
334
|
432
|
|
|
|
|
776
|
my ($key, $col); |
335
|
432
|
50
|
66
|
|
|
2101
|
if ($self && @{$class->retrieve_keys || $class->primary_keys}) { |
|
66
|
100
|
|
|
|
388
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
336
|
66
|
50
|
|
|
|
589
|
if ($self->has_muid) { |
337
|
0
|
|
|
|
|
0
|
$key = $self->muid; |
338
|
|
|
|
|
|
|
} else { |
339
|
66
|
50
|
|
|
|
140
|
for (sort @{$class->retrieve_keys || $class->primary_keys}) { |
|
66
|
|
|
|
|
997
|
|
340
|
76
|
50
|
0
|
|
|
596
|
defined($self->{$_}) or warn "$_ is undefined for $self" and return; |
341
|
76
|
|
|
|
|
1084
|
$key .= "-$_-" . $self->{$_}; |
342
|
|
|
|
|
|
|
} |
343
|
66
|
50
|
|
|
|
256
|
$key or die "couldn't create object_id for " . $self; |
344
|
66
|
|
|
|
|
268
|
$key = $prefix . $key; |
345
|
|
|
|
|
|
|
} |
346
|
281
|
|
|
|
|
1010
|
} elsif ($_[3]) { |
347
|
85
|
|
|
|
|
252
|
my %args = @_; |
348
|
85
|
|
|
|
|
690
|
$key .= "-$_-$args{$_}" for (sort keys %args); |
349
|
85
|
|
|
|
|
293
|
$key = $prefix . $key; |
350
|
|
|
|
|
|
|
} elsif (@{$class->primary_keys} == 1) { |
351
|
257
|
|
|
|
|
1381
|
my @args = @_; |
352
|
257
|
100
|
|
|
|
781
|
$col = defined $args[1] ? $args[0] : $class->primary_keys->[0]; |
353
|
257
|
100
|
|
|
|
864
|
my $value = defined $args[1] ? $args[1] : $args[0]; |
354
|
257
|
50
|
|
|
|
610
|
if ($col eq 'muid') { |
355
|
0
|
|
|
|
|
0
|
$key = $value; |
356
|
|
|
|
|
|
|
} else { |
357
|
257
|
|
|
|
|
1069
|
$key = $prefix . '-' . $col . '-' . $value; |
358
|
|
|
|
|
|
|
} |
359
|
|
|
|
|
|
|
} |
360
|
432
|
100
|
|
|
|
2096
|
$self->{object_id} = $key if $self;; |
361
|
432
|
|
|
|
|
2579
|
return $key; |
362
|
|
|
|
|
|
|
} |
363
|
|
|
|
|
|
|
|
364
|
|
|
|
|
|
|
sub object_id_prefix { |
365
|
432
|
|
|
432
|
1
|
739
|
my $class = shift; |
366
|
432
|
50
|
|
|
|
1044
|
$class = ref $class if ref $class; |
367
|
432
|
|
|
|
|
1540
|
return $class; |
368
|
|
|
|
|
|
|
} |
369
|
|
|
|
|
|
|
|
370
|
193
|
|
|
193
|
0
|
1045
|
sub db { $_[0]->db_object } |
371
|
|
|
|
|
|
|
|
372
|
|
|
|
|
|
|
sub retrieve { |
373
|
97
|
|
|
97
|
1
|
29062
|
my $cs = $cache_status; |
374
|
97
|
|
|
|
|
329
|
$cs->{retrieve_count}++; |
375
|
97
|
|
|
|
|
186
|
my $class = shift; |
376
|
97
|
50
|
|
|
|
339
|
$_[0] or carp "Retrieve keys not found"; |
377
|
97
|
|
|
|
|
701
|
my $oid = $class->object_id(@_); |
378
|
97
|
|
|
|
|
470
|
my $c = $class->cache($oid); |
379
|
97
|
100
|
|
|
|
281
|
if (defined $c) { |
380
|
|
|
|
|
|
|
# warn "use cache $oid"; |
381
|
47
|
|
|
|
|
114
|
$cs->{retrieve_cache_count}++; |
382
|
47
|
|
|
|
|
225
|
return $c; |
383
|
|
|
|
|
|
|
} else { |
384
|
|
|
|
|
|
|
# warn "use db $oid"; |
385
|
50
|
|
|
|
|
301
|
my $o = $class->retrieve_by_db(@_); |
386
|
50
|
100
|
|
|
|
368
|
if ($o) { |
387
|
33
|
|
|
|
|
212
|
$class->store_self_cache($o); |
388
|
33
|
100
|
|
|
|
104
|
push @{$cs->{retrieved_oids}}, $oid if $class->is_in_session; |
|
1
|
|
|
|
|
5
|
|
389
|
|
|
|
|
|
|
} else { |
390
|
|
|
|
|
|
|
# $class->cache($oid => $o) if $o; |
391
|
|
|
|
|
|
|
# cache null object for performance. |
392
|
17
|
50
|
|
|
|
139
|
$class->cache($oid => $o) if $class->cache_null_object; |
393
|
|
|
|
|
|
|
} |
394
|
50
|
|
|
|
|
356
|
return $o; |
395
|
|
|
|
|
|
|
} |
396
|
|
|
|
|
|
|
} |
397
|
|
|
|
|
|
|
|
398
|
|
|
|
|
|
|
sub retrieve_by_db { |
399
|
50
|
|
|
50
|
1
|
102
|
my $class = shift; |
400
|
50
|
100
|
|
|
|
258
|
my %args = defined $_[1] ? @_ : ($class->primary_keys->[0] => $_[0]); |
401
|
50
|
|
|
|
|
373
|
my $res = $class->db->select($class->table,'*',\%args); |
402
|
50
|
|
|
|
|
146
|
my $h = $res->[0]; |
403
|
50
|
100
|
|
|
|
476
|
return $h ? $class->new(%$h) : ''; |
404
|
|
|
|
|
|
|
} |
405
|
|
|
|
|
|
|
|
406
|
|
|
|
|
|
|
sub restore_from_db { |
407
|
1
|
|
|
1
|
1
|
5
|
my $self = shift; |
408
|
1
|
50
|
|
|
|
6
|
my $class = ref $self or return; |
409
|
1
|
50
|
|
|
|
10
|
my $hash = $self->primary_keys_hash or return; |
410
|
1
|
|
|
|
|
7
|
my $res = $class->db->select($class->table,'*',$hash); |
411
|
1
|
50
|
|
|
|
6
|
my $h = $res->[0] or return; |
412
|
1
|
|
|
|
|
4
|
@{$self}{keys %$h} = @{$h}{keys %$h}; |
|
1
|
|
|
|
|
4
|
|
|
1
|
|
|
|
|
3
|
|
413
|
1
|
|
|
|
|
7
|
$class->store_self_cache($self); |
414
|
1
|
|
|
|
|
5
|
return $self; |
415
|
|
|
|
|
|
|
} |
416
|
|
|
|
|
|
|
|
417
|
|
|
|
|
|
|
sub retrieve_multi { |
418
|
27
|
|
|
27
|
1
|
48
|
my $class = shift; |
419
|
27
|
100
|
|
|
|
110
|
my @list = @_ or return $class->_list([]); |
420
|
|
|
|
|
|
|
|
421
|
26
|
|
|
|
|
44
|
my (@cached_objects, @non_cached_queries); |
422
|
26
|
50
|
33
|
|
|
110
|
if ($class->cache_object && $class->cache_object->can('get_multi')) { |
423
|
0
|
|
|
|
|
0
|
my $ids = [ map { $class->object_id(%$_) } @list ]; |
|
0
|
|
|
|
|
0
|
|
424
|
0
|
|
0
|
|
|
0
|
my $hash = $class->cache_object->get_multi(@$ids) || {}; |
425
|
|
|
|
|
|
|
|
426
|
0
|
|
|
|
|
0
|
for (my $i = 0; $i <= $#list; $i++) { |
427
|
0
|
|
|
|
|
0
|
my $object = $hash->{$ids->[$i]}; |
428
|
0
|
0
|
|
|
|
0
|
$object |
429
|
|
|
|
|
|
|
? push @cached_objects, $object |
430
|
|
|
|
|
|
|
: push @non_cached_queries, $list[$i]; |
431
|
|
|
|
|
|
|
} |
432
|
|
|
|
|
|
|
} else { |
433
|
26
|
|
|
|
|
613
|
for (@list) { |
434
|
52
|
|
|
|
|
233
|
my $cached_object = $class->cache( $class->object_id(%$_) ); |
435
|
52
|
100
|
|
|
|
241
|
$cached_object |
436
|
|
|
|
|
|
|
? push @cached_objects, $cached_object |
437
|
|
|
|
|
|
|
: push @non_cached_queries, $_; |
438
|
|
|
|
|
|
|
} |
439
|
|
|
|
|
|
|
} |
440
|
|
|
|
|
|
|
|
441
|
|
|
|
|
|
|
## Updating cache status |
442
|
26
|
|
|
|
|
124
|
$class->cache_status->{retrieve_count} += scalar @list; |
443
|
26
|
|
|
|
|
67
|
$class->cache_status->{retrieve_cache_count} += scalar @cached_objects; |
444
|
|
|
|
|
|
|
|
445
|
|
|
|
|
|
|
## All objects were found in cache. |
446
|
26
|
100
|
|
|
|
84
|
if (@cached_objects == @list) { |
447
|
21
|
|
|
|
|
110
|
my @ordered= $class->_merge_objects(\@list, @cached_objects); |
448
|
21
|
50
|
|
|
|
589
|
wantarray ? return @ordered : return $class->_list(\@ordered); |
449
|
|
|
|
|
|
|
} |
450
|
|
|
|
|
|
|
|
451
|
5
|
|
|
|
|
12
|
my (@clauses, @bind_values); |
452
|
5
|
|
|
|
|
15
|
for my $cond (@non_cached_queries) { |
453
|
12
|
|
|
|
|
44
|
my $subclause = join ' AND ', map { |
454
|
7
|
|
|
|
|
24
|
push @bind_values, $cond->{$_}; |
455
|
12
|
|
|
|
|
60
|
sprintf "%s = ?", $_ |
456
|
|
|
|
|
|
|
} keys %$cond; |
457
|
|
|
|
|
|
|
|
458
|
7
|
|
|
|
|
27
|
push @clauses, $subclause; |
459
|
|
|
|
|
|
|
} |
460
|
5
|
|
|
|
|
13
|
my $where_clause = join ' OR ', map { sprintf "(%s)", $_ } @clauses; |
|
7
|
|
|
|
|
31
|
|
461
|
|
|
|
|
|
|
|
462
|
5
|
|
|
|
|
51
|
my @objects_from_db = $class->search( where => [ $where_clause, @bind_values ] ); |
463
|
|
|
|
|
|
|
|
464
|
5
|
50
|
|
|
|
28
|
if ($class->is_in_session) { |
465
|
0
|
|
|
|
|
0
|
push @{$class->cache_status->{retrieved_oids}}, map { $_->object_id } @objects_from_db; |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
466
|
|
|
|
|
|
|
} |
467
|
|
|
|
|
|
|
|
468
|
5
|
|
|
|
|
15
|
for my $object (@objects_from_db) { |
469
|
7
|
|
|
|
|
41
|
$class->store_self_cache($object); |
470
|
|
|
|
|
|
|
} |
471
|
|
|
|
|
|
|
|
472
|
5
|
|
|
|
|
44
|
my @merged = $class->_merge_objects(\@list, @cached_objects, @objects_from_db); |
473
|
5
|
50
|
|
|
|
135
|
wantarray ? return @merged : return $class->_list(\@merged); |
474
|
|
|
|
|
|
|
} |
475
|
|
|
|
|
|
|
|
476
|
|
|
|
|
|
|
sub _merge_objects { |
477
|
26
|
|
|
26
|
|
59
|
my $class = shift; |
478
|
26
|
|
|
|
|
42
|
my $order = shift; |
479
|
|
|
|
|
|
|
|
480
|
26
|
|
|
|
|
261
|
my $tied = tie my %idt, 'Tie::IxHash'; ## orderd Hash |
481
|
26
|
|
|
|
|
497
|
$tied->Push($class->object_id( %$_ ) => undef) for @$order; |
482
|
|
|
|
|
|
|
|
483
|
26
|
|
|
|
|
685
|
for (@_) { |
484
|
52
|
|
|
|
|
537
|
my $id = $_->object_id; |
485
|
52
|
50
|
|
|
|
276
|
die "assert" if not exists $idt{$id}; |
486
|
52
|
|
|
|
|
436
|
$tied->Push($id => $_); |
487
|
|
|
|
|
|
|
} |
488
|
|
|
|
|
|
|
|
489
|
|
|
|
|
|
|
## cache_null_object() is now deprecated |
490
|
|
|
|
|
|
|
# for (keys %idt) { |
491
|
|
|
|
|
|
|
# if (not $idt{$_} and $class->cache_null_object) { |
492
|
|
|
|
|
|
|
# $class->cache( $_ => '' ); |
493
|
|
|
|
|
|
|
# } |
494
|
|
|
|
|
|
|
# } |
495
|
|
|
|
|
|
|
|
496
|
26
|
|
|
|
|
635
|
grep { defined $_ } values %idt; |
|
52
|
|
|
|
|
940
|
|
497
|
|
|
|
|
|
|
} |
498
|
|
|
|
|
|
|
|
499
|
|
|
|
|
|
|
sub retrieve_or_create { |
500
|
2
|
|
|
2
|
1
|
172
|
my $class = shift; |
501
|
2
|
|
|
|
|
9
|
my %args = @_; |
502
|
2
|
|
|
|
|
6
|
my %keys; |
503
|
2
|
|
|
|
|
5
|
@keys{@{$class->primary_keys}} = @args{@{$class->primary_keys}}; |
|
2
|
|
|
|
|
15
|
|
|
2
|
|
|
|
|
10
|
|
504
|
2
|
100
|
|
|
|
29
|
$class->retrieve(%keys) || $class->create(%args); |
505
|
|
|
|
|
|
|
} |
506
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
sub retrieve_all { |
508
|
4
|
|
|
4
|
1
|
2106
|
my $cs = $cache_status; |
509
|
4
|
|
|
|
|
11
|
$cs->{retrieve_all_count}++; |
510
|
4
|
|
|
|
|
9
|
my $class = shift; |
511
|
4
|
|
|
|
|
19
|
my %args = @_; |
512
|
4
|
|
|
|
|
11
|
my $result = []; |
513
|
4
|
|
|
|
|
25
|
my $list = $class->retrieve_all_id_hash(%args); |
514
|
4
|
|
|
|
|
34
|
push @$result, $class->retrieve(%$_) for (@$list); |
515
|
4
|
50
|
|
|
|
24
|
wantarray ? @$result : |
516
|
|
|
|
|
|
|
$class->_list($result); |
517
|
|
|
|
|
|
|
} |
518
|
|
|
|
|
|
|
|
519
|
|
|
|
|
|
|
sub retrieve_all_id_hash { |
520
|
6
|
|
|
6
|
0
|
165
|
my $class = shift; |
521
|
6
|
|
|
|
|
18
|
my %args = @_; |
522
|
6
|
|
|
|
|
29
|
$args{table} = $class->table; |
523
|
6
|
50
|
|
|
|
69
|
$args{field} = join(',', @{$class->retrieve_keys || $class->primary_keys}); |
|
6
|
|
|
|
|
103
|
|
524
|
6
|
|
|
|
|
442
|
my $res = $class->db->search(%args); |
525
|
6
|
|
|
|
|
27
|
return $res; |
526
|
|
|
|
|
|
|
} |
527
|
|
|
|
|
|
|
|
528
|
|
|
|
|
|
|
sub create { |
529
|
26
|
|
|
26
|
1
|
5632
|
my $class = shift; |
530
|
26
|
|
|
|
|
112
|
my %args = @_; |
531
|
26
|
|
|
|
|
250
|
$class->call_trigger('before_create', \%args); |
532
|
26
|
|
|
|
|
1350
|
my $o = $class->new(%args); |
533
|
|
|
|
|
|
|
# if ($class->is_in_session && $o->has_primary_keys) { |
534
|
|
|
|
|
|
|
# $o->set(to_be_inserted => 1); |
535
|
|
|
|
|
|
|
# $o->changed_cols->{$_}++ for (keys %args); |
536
|
|
|
|
|
|
|
# push @{$class->session->{changed_objects}}, $o; |
537
|
|
|
|
|
|
|
# } else { |
538
|
26
|
50
|
|
|
|
305
|
if ($class->save_explicitly) { |
539
|
0
|
|
|
|
|
0
|
$o->set(to_be_inserted => 1); |
540
|
0
|
|
|
|
|
0
|
$o->changed_cols->{$_}++ for keys %args; |
541
|
|
|
|
|
|
|
} else { |
542
|
26
|
50
|
|
|
|
408
|
$class->db->insert($class->table,\%args) or croak 'couldnt create'; |
543
|
26
|
|
|
|
|
385
|
my $pk = $class->primary_keys->[0]; |
544
|
26
|
100
|
|
|
|
370
|
unless (defined $args{$pk}) { |
545
|
6
|
|
|
|
|
545
|
my $id = $class->db->last_insert_id; |
546
|
6
|
|
|
|
|
205
|
$o->set($pk => $id); |
547
|
|
|
|
|
|
|
} |
548
|
|
|
|
|
|
|
} |
549
|
26
|
|
|
|
|
285
|
$class->call_trigger('after_create', $o); |
550
|
26
|
|
|
|
|
666
|
return $o; |
551
|
|
|
|
|
|
|
} |
552
|
|
|
|
|
|
|
|
553
|
|
|
|
|
|
|
sub delete { |
554
|
10
|
|
|
10
|
1
|
675
|
my $self = shift; |
555
|
10
|
100
|
|
|
|
48
|
my $class = ref($self) ? ref($self) : $self; |
556
|
10
|
100
|
|
|
|
39
|
$self = shift unless ref($self); |
557
|
10
|
50
|
|
|
|
62
|
$self or return; |
558
|
10
|
|
|
|
|
80
|
$self->call_trigger('before_delete', $self); |
559
|
10
|
50
|
|
|
|
196
|
$self->has_primary_keys or return; |
560
|
10
|
|
|
|
|
20
|
my %args; |
561
|
10
|
|
|
|
|
19
|
for (@{$class->primary_keys}) { |
|
10
|
|
|
|
|
33
|
|
562
|
10
|
|
|
|
|
55
|
$args{$_} = $self->{$_}; |
563
|
10
|
50
|
|
|
|
45
|
defined($args{$_}) or die "$self doesn't have $_"; |
564
|
|
|
|
|
|
|
} |
565
|
10
|
50
|
|
|
|
34
|
%args or die "$self doesn't have where condition"; |
566
|
10
|
50
|
|
|
|
47
|
my $res = $class->db->delete($class->table,\%args) or croak 'couldnt delete'; |
567
|
10
|
|
|
|
|
48
|
$self = undef; |
568
|
10
|
|
|
|
|
273
|
return $res; |
569
|
|
|
|
|
|
|
} |
570
|
|
|
|
|
|
|
|
571
|
|
|
|
|
|
|
sub delete_all { |
572
|
1
|
|
|
1
|
1
|
3
|
my $class = shift; |
573
|
1
|
|
|
|
|
12
|
my %args = @_; |
574
|
1
|
50
|
|
|
|
10
|
ref $args{where} eq 'HASH' or die 'please specify where in hash'; |
575
|
1
|
|
|
|
|
11
|
my $list = $class->retrieve_all_id_hash(%args); |
576
|
1
|
|
|
|
|
5
|
my $caches = []; |
577
|
1
|
|
|
|
|
4
|
for (@$list) { |
578
|
2
|
|
|
|
|
13
|
my $oid = $class->object_id(%$_); |
579
|
2
|
50
|
|
|
|
9
|
my $c = $class->cache($oid) or next; |
580
|
2
|
|
|
|
|
9
|
push @$caches, $c; |
581
|
|
|
|
|
|
|
} |
582
|
1
|
|
|
|
|
8
|
$class->call_trigger('before_delete', $_) for (@$caches); |
583
|
1
|
50
|
|
|
|
21
|
$class->db->delete($class->table,$args{where}) or croak 'couldnt delete'; |
584
|
1
|
|
|
|
|
34
|
return 1; |
585
|
|
|
|
|
|
|
} |
586
|
|
|
|
|
|
|
|
587
|
|
|
|
|
|
|
sub search { |
588
|
21
|
|
|
21
|
1
|
2947
|
my $class = shift; |
589
|
21
|
|
|
|
|
182
|
my %args = @_; |
590
|
|
|
|
|
|
|
|
591
|
21
|
|
|
|
|
57
|
my $with = delete $args{with}; |
592
|
|
|
|
|
|
|
|
593
|
21
|
|
|
|
|
204
|
$args{table} = $class->table; |
594
|
21
|
|
|
|
|
355
|
my $res = $class->db->search(%args); |
595
|
21
|
|
|
|
|
216
|
$_ = $class->new(%$_) for @$res; |
596
|
21
|
50
|
|
|
|
79
|
$class->merge_with($res, $with) if $with; |
597
|
|
|
|
|
|
|
|
598
|
21
|
100
|
|
|
|
184
|
wantarray ? @$res : $class->_list($res); |
599
|
|
|
|
|
|
|
} |
600
|
|
|
|
|
|
|
|
601
|
|
|
|
|
|
|
sub merge_with { |
602
|
0
|
|
|
0
|
0
|
0
|
my ($class, $res, $with, $without) = @_; |
603
|
|
|
|
|
|
|
|
604
|
0
|
0
|
0
|
|
|
0
|
my @with_attrs = (ref $with and ref $with eq 'ARRAY') ? @$with : $with; |
605
|
|
|
|
|
|
|
|
606
|
0
|
0
|
|
|
|
0
|
if ($without) { |
607
|
0
|
0
|
0
|
|
|
0
|
my @withouts = (ref $without and ref $without eq 'ARRAY') ? @$without : $without; |
608
|
0
|
|
|
|
|
0
|
my $regex = sprintf '(?:^%s$)', join '|', @withouts; |
609
|
0
|
|
|
|
|
0
|
@with_attrs = grep { $_ !~ m/$regex/ } @with_attrs; |
|
0
|
|
|
|
|
0
|
|
610
|
|
|
|
|
|
|
} |
611
|
|
|
|
|
|
|
|
612
|
0
|
|
|
|
|
0
|
for my $with_attr (@with_attrs) { |
613
|
0
|
0
|
|
|
|
0
|
my $rel = $class->relation->find_relation_by_attr($class => $with_attr) |
614
|
|
|
|
|
|
|
or croak "No such relation for attr '$with_attr' in $class"; |
615
|
|
|
|
|
|
|
|
616
|
0
|
0
|
|
|
|
0
|
my $key = $rel->{option}->{key} or next; |
617
|
|
|
|
|
|
|
|
618
|
0
|
|
|
|
|
0
|
my ($my_key, $other_key); |
619
|
0
|
0
|
0
|
|
|
0
|
(ref $key and ref $key eq 'HASH') |
620
|
|
|
|
|
|
|
? ($my_key, $other_key) = %$key |
621
|
|
|
|
|
|
|
: $my_key = $other_key = $key; |
622
|
|
|
|
|
|
|
|
623
|
0
|
|
|
|
|
0
|
my @queries = map { +{ $other_key => $_->$my_key } } @$res; |
|
0
|
|
|
|
|
0
|
|
624
|
|
|
|
|
|
|
|
625
|
|
|
|
|
|
|
## Only creating caches for less SQL queries. |
626
|
|
|
|
|
|
|
## Those caches will be stored to the session cache if the session is activated. |
627
|
0
|
|
|
|
|
0
|
$rel->{class}->retrieve_multi(@queries); |
628
|
|
|
|
|
|
|
} |
629
|
|
|
|
|
|
|
|
630
|
0
|
|
|
|
|
0
|
$res; |
631
|
|
|
|
|
|
|
} |
632
|
|
|
|
|
|
|
|
633
|
|
|
|
|
|
|
sub count { |
634
|
7
|
|
|
7
|
1
|
2513
|
my $class = shift; |
635
|
7
|
|
|
|
|
16
|
my $where = ''; |
636
|
7
|
50
|
|
|
|
83
|
if ($_[1]) { |
|
|
100
|
|
|
|
|
|
637
|
0
|
|
|
|
|
0
|
my %args = @_; |
638
|
0
|
|
|
|
|
0
|
$where = \%args; |
639
|
|
|
|
|
|
|
} elsif ($_[0]) { |
640
|
3
|
|
|
|
|
7
|
$where = shift; |
641
|
|
|
|
|
|
|
} |
642
|
7
|
|
|
|
|
39
|
my $res = $class->db->search( |
643
|
|
|
|
|
|
|
table => $class->table, |
644
|
|
|
|
|
|
|
field => 'COUNT(*) as count', |
645
|
|
|
|
|
|
|
where => $where, |
646
|
|
|
|
|
|
|
); |
647
|
7
|
|
50
|
|
|
73
|
return $res->[0]->{count} || 0; |
648
|
|
|
|
|
|
|
} |
649
|
|
|
|
|
|
|
|
650
|
|
|
|
|
|
|
sub find { |
651
|
2
|
|
|
2
|
1
|
362
|
my $class = shift; |
652
|
2
|
|
|
|
|
4
|
my $where; |
653
|
2
|
50
|
|
|
|
15
|
if ($_[1]) { |
|
|
50
|
|
|
|
|
|
654
|
0
|
|
|
|
|
0
|
my %args = @_; |
655
|
0
|
|
|
|
|
0
|
$where = \%args; |
656
|
|
|
|
|
|
|
} elsif ($_[0]) { |
657
|
2
|
|
|
|
|
6
|
$where = shift; |
658
|
|
|
|
|
|
|
} else { |
659
|
0
|
|
|
|
|
0
|
return; |
660
|
|
|
|
|
|
|
} |
661
|
2
|
|
|
|
|
19
|
$class->search( |
662
|
|
|
|
|
|
|
where => $where, |
663
|
|
|
|
|
|
|
offset => 0, |
664
|
|
|
|
|
|
|
limit => 1, |
665
|
|
|
|
|
|
|
)->first; |
666
|
|
|
|
|
|
|
} |
667
|
|
|
|
|
|
|
|
668
|
|
|
|
|
|
|
sub quote { |
669
|
3
|
|
|
3
|
1
|
2714
|
my $class = shift; |
670
|
3
|
|
|
|
|
14
|
$class->db->dbh->quote(shift); |
671
|
|
|
|
|
|
|
} |
672
|
|
|
|
|
|
|
|
673
|
|
|
|
|
|
|
sub scalar { |
674
|
0
|
|
|
0
|
0
|
0
|
my ($class, $method, @args) = @_; |
675
|
0
|
|
|
|
|
0
|
scalar $class->$method(@args); |
676
|
|
|
|
|
|
|
} |
677
|
|
|
|
|
|
|
|
678
|
|
|
|
|
|
|
sub AUTOLOAD { |
679
|
43
|
|
|
43
|
|
15106
|
my $self = $_[0]; |
680
|
43
|
|
66
|
|
|
188
|
my $class = ref($self) || $self; |
681
|
43
|
100
|
|
|
|
139
|
$self = undef unless ref($self); |
682
|
43
|
|
|
|
|
411
|
(my $method = $AUTOLOAD) =~ s!.+::!!; |
683
|
43
|
50
|
|
|
|
295
|
return if $method eq 'DESTROY'; |
684
|
15
|
|
|
15
|
|
130
|
no strict 'refs'; |
|
15
|
|
|
|
|
38
|
|
|
15
|
|
|
|
|
6384
|
|
685
|
43
|
100
|
100
|
|
|
412
|
if ($method =~ /^retrieve_by_(.+?)(_or_create)?$/o) { |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
686
|
8
|
|
|
|
|
34
|
my ($by, $create) = ($1,$2); |
687
|
8
|
100
|
|
|
|
68
|
*$AUTOLOAD = $create ? $class->_retrieve_by_or_create_handler($by) : |
688
|
|
|
|
|
|
|
$class->_retrieve_by_handler($by); |
689
|
|
|
|
|
|
|
} elsif ($method =~ /^(\w+)_as_(\w+)$/o) { |
690
|
7
|
|
|
|
|
33
|
my ($col,$as) = ($1,$2); |
691
|
7
|
|
|
|
|
57
|
*$AUTOLOAD = $class->_column_as_handler($col, $as); |
692
|
|
|
|
|
|
|
} elsif (defined $self->{$method} || $class->has_column($method)) { |
693
|
24
|
|
|
111
|
|
246
|
*$AUTOLOAD = sub { shift->param($method, @_) }; |
|
111
|
|
|
|
|
24006
|
|
694
|
|
|
|
|
|
|
} else { |
695
|
4
|
|
|
|
|
1035
|
croak sprintf 'Can\'t locate object method "%s" via package %s', $method, $class; |
696
|
|
|
|
|
|
|
} |
697
|
39
|
|
|
|
|
251
|
goto &$AUTOLOAD; |
698
|
|
|
|
|
|
|
} |
699
|
|
|
|
|
|
|
|
700
|
|
|
|
|
|
|
sub inflate_column { |
701
|
1
|
|
|
1
|
0
|
723
|
my $class = shift; |
702
|
1
|
50
|
|
|
|
7
|
@_ % 2 and croak "You gave me an odd number of parameters to inflate_column()"; |
703
|
|
|
|
|
|
|
|
704
|
1
|
|
|
|
|
6
|
my %args = @_; |
705
|
1
|
|
|
|
|
14
|
while (my ($col, $as) = each %args) { |
706
|
15
|
|
|
15
|
|
124
|
no strict 'refs'; |
|
15
|
|
|
|
|
42
|
|
|
15
|
|
|
|
|
487
|
|
707
|
15
|
|
|
15
|
|
132
|
no warnings 'redefine'; |
|
15
|
|
|
|
|
31
|
|
|
15
|
|
|
|
|
5136
|
|
708
|
|
|
|
|
|
|
|
709
|
2
|
100
|
66
|
|
|
19
|
if (ref $as and ref $as eq 'HASH') { |
710
|
1
|
|
|
|
|
4
|
for (qw/inflate deflate/) { |
711
|
2
|
50
|
33
|
|
|
20
|
if ($as->{$_} and ref $as->{$_} ne 'CODE') { |
712
|
0
|
|
|
|
|
0
|
croak sprintf "parameter '%s' takes only CODE reference", $_ |
713
|
|
|
|
|
|
|
} |
714
|
|
|
|
|
|
|
} |
715
|
|
|
|
|
|
|
|
716
|
1
|
|
|
|
|
17
|
*{"$class\::$col"} = sub { |
717
|
6
|
|
|
6
|
|
36816
|
my $self = shift; |
718
|
6
|
100
|
|
|
|
29
|
if (@_) { |
719
|
1
|
50
|
|
|
|
14
|
$as->{deflate} |
720
|
|
|
|
|
|
|
? $self->param( $col => $as->{deflate}->(@_) ) |
721
|
|
|
|
|
|
|
: $self->param( $col => @_ ); |
722
|
|
|
|
|
|
|
} else { |
723
|
5
|
50
|
|
|
|
38
|
$as->{inflate} |
724
|
|
|
|
|
|
|
? $as->{inflate}->( $self->param($col) ) |
725
|
|
|
|
|
|
|
: $self->param( $col ); |
726
|
|
|
|
|
|
|
} |
727
|
|
|
|
|
|
|
} |
728
|
1
|
|
|
|
|
7
|
} else { |
729
|
1
|
|
|
|
|
9
|
*{"$class\::$col"} = $class->_column_as_handler($col, $as); |
|
1
|
|
|
|
|
21
|
|
730
|
|
|
|
|
|
|
} |
731
|
|
|
|
|
|
|
} |
732
|
|
|
|
|
|
|
} |
733
|
|
|
|
|
|
|
|
734
|
|
|
|
|
|
|
{ |
735
|
|
|
|
|
|
|
my $real_can = \&UNIVERSAL::can; |
736
|
15
|
|
|
15
|
|
597
|
no warnings 'redefine', 'once'; |
|
15
|
|
|
|
|
29
|
|
|
15
|
|
|
|
|
1377
|
|
737
|
|
|
|
|
|
|
*DBIx::MoCo::can = sub { |
738
|
106
|
|
|
106
|
|
5886
|
my ($class, $method) = @_; |
739
|
106
|
100
|
|
|
|
800
|
if (my $sub = $real_can->(@_)) { |
740
|
|
|
|
|
|
|
# warn "found $method in $class"; |
741
|
99
|
|
|
|
|
354
|
return $sub; |
742
|
|
|
|
|
|
|
} |
743
|
15
|
|
|
15
|
|
83
|
no strict 'refs'; |
|
15
|
|
|
|
|
29
|
|
|
15
|
|
|
|
|
3820
|
|
744
|
7
|
50
|
|
|
|
10
|
if (my $auto = *{$class . '::AUTOLOAD'}{CODE}) { |
|
7
|
|
|
|
|
59
|
|
745
|
0
|
|
|
|
|
0
|
return $auto; |
746
|
|
|
|
|
|
|
} |
747
|
7
|
|
|
|
|
24
|
$AUTOLOAD = $class . '::' . $method; |
748
|
7
|
50
|
|
|
|
38
|
eval {&DBIx::MoCo::AUTOLOAD(@_)} unless *$AUTOLOAD{CODE}; |
|
7
|
|
|
|
|
24
|
|
749
|
7
|
|
|
|
|
232
|
return *$AUTOLOAD{CODE}; |
750
|
|
|
|
|
|
|
}; |
751
|
|
|
|
|
|
|
} |
752
|
|
|
|
|
|
|
|
753
|
|
|
|
|
|
|
sub _column_as_handler { |
754
|
10
|
|
|
10
|
|
23
|
my $class = shift; |
755
|
10
|
|
|
|
|
29
|
my ($colname, $as) = @_; |
756
|
10
|
100
|
|
|
|
174
|
unless (DBIx::MoCo::Column->can($as)) { |
757
|
7
|
|
|
|
|
25
|
my $plugin = "DBIx::MoCo::Column::$as"; |
758
|
7
|
|
|
|
|
69
|
$plugin->require; |
759
|
7
|
50
|
|
|
|
141
|
croak "Couldn't load column plugin $plugin: $@" if $@; |
760
|
|
|
|
|
|
|
{ |
761
|
15
|
|
|
15
|
|
86
|
no strict 'refs'; |
|
15
|
|
|
|
|
31
|
|
|
15
|
|
|
|
|
26838
|
|
|
7
|
|
|
|
|
15
|
|
762
|
7
|
|
|
|
|
17
|
push @{"DBIx::MoCo::Column::ISA"}, $plugin; |
|
7
|
|
|
|
|
178
|
|
763
|
|
|
|
|
|
|
} |
764
|
|
|
|
|
|
|
} |
765
|
|
|
|
|
|
|
return sub { |
766
|
23
|
|
|
23
|
|
64666
|
my $self = shift; |
767
|
23
|
50
|
|
|
|
127
|
my $column = $self->column($colname) or return; |
768
|
22
|
100
|
|
|
|
92
|
if (my $new = shift) { |
769
|
6
|
|
|
|
|
243
|
my $as_string = $as . '_as_string'; # e.g. URI_as_string |
770
|
6
|
50
|
|
|
|
112
|
my $v = $column->can($as_string) ? |
771
|
|
|
|
|
|
|
$column->$as_string($new) : scalar $new; |
772
|
6
|
|
|
|
|
395
|
$self->param($colname => $v); |
773
|
|
|
|
|
|
|
} |
774
|
22
|
|
|
|
|
90
|
$self->column($colname)->$as(); |
775
|
|
|
|
|
|
|
} |
776
|
10
|
|
|
|
|
154
|
} |
777
|
|
|
|
|
|
|
|
778
|
|
|
|
|
|
|
sub column { |
779
|
46
|
|
|
46
|
0
|
3529
|
my $self = shift; |
780
|
46
|
50
|
|
|
|
157
|
my $col = shift or return; |
781
|
46
|
|
|
|
|
380
|
return DBIx::MoCo::Column->new($self->{$col}); |
782
|
|
|
|
|
|
|
} |
783
|
|
|
|
|
|
|
|
784
|
|
|
|
|
|
|
sub _retrieve_by_handler { |
785
|
6
|
|
|
6
|
|
13
|
my $class = shift; |
786
|
6
|
50
|
|
|
|
21
|
my $by = shift or return; |
787
|
6
|
100
|
|
|
|
33
|
if ($by =~ /.+_or_.+/) { |
788
|
2
|
|
|
|
|
10
|
my @keys = split('_or_', $by); |
789
|
|
|
|
|
|
|
return sub { |
790
|
3
|
|
|
3
|
|
8
|
my $self = shift; |
791
|
3
|
|
|
|
|
6
|
my $v = shift; |
792
|
3
|
|
|
|
|
7
|
for (@keys) { |
793
|
4
|
|
|
|
|
16
|
my $o = $self->retrieve($_ => $v); |
794
|
4
|
100
|
|
|
|
20
|
return $o if $o; |
795
|
|
|
|
|
|
|
} |
796
|
2
|
|
|
|
|
18
|
}; |
797
|
|
|
|
|
|
|
} else { |
798
|
4
|
|
|
|
|
20
|
my @keys = split('_and_', $by); |
799
|
|
|
|
|
|
|
return sub { |
800
|
6
|
|
|
6
|
|
185
|
my $self = shift; |
801
|
6
|
|
|
|
|
12
|
my %args; |
802
|
6
|
|
|
|
|
28
|
@args{@keys} = @_; |
803
|
6
|
|
|
|
|
42
|
$self->retrieve(%args); |
804
|
4
|
|
|
|
|
40
|
}; |
805
|
|
|
|
|
|
|
} |
806
|
|
|
|
|
|
|
} |
807
|
|
|
|
|
|
|
|
808
|
|
|
|
|
|
|
sub _retrieve_by_or_create_handler { |
809
|
2
|
|
|
2
|
|
6
|
my $class = shift; |
810
|
2
|
50
|
|
|
|
10
|
my $by = shift or return; |
811
|
2
|
|
|
|
|
9
|
my @keys = split('_and_', $by); |
812
|
|
|
|
|
|
|
return sub { |
813
|
3
|
|
|
3
|
|
7
|
my $self = shift; |
814
|
3
|
|
|
|
|
6
|
my %args; |
815
|
3
|
|
|
|
|
11
|
@args{@keys} = @_; |
816
|
3
|
|
66
|
|
|
16
|
return $self->retrieve(%args) || $class->create(%args); |
817
|
2
|
|
|
|
|
18
|
}; |
818
|
|
|
|
|
|
|
} |
819
|
|
|
|
|
|
|
|
820
|
|
|
|
|
|
|
sub _list { |
821
|
36
|
|
|
36
|
|
72
|
my $class = shift; |
822
|
|
|
|
|
|
|
|
823
|
36
|
100
|
|
|
|
203
|
if ($class->list_class) { |
824
|
1
|
|
|
|
|
10
|
$class->list_class->require; |
825
|
1
|
50
|
33
|
|
|
19
|
if ($@ and $@ !~ m/^Can\'t locate .+? in \@INC/) { |
826
|
0
|
|
|
|
|
0
|
die $@; |
827
|
|
|
|
|
|
|
} |
828
|
1
|
|
|
|
|
3
|
return $class->list_class->new(@_); |
829
|
|
|
|
|
|
|
} else { |
830
|
35
|
|
|
|
|
548
|
return DBIx::MoCo::List->new(@_); |
831
|
|
|
|
|
|
|
} |
832
|
|
|
|
|
|
|
} |
833
|
|
|
|
|
|
|
|
834
|
|
|
|
|
|
|
sub DESTROY { |
835
|
107
|
|
|
107
|
|
34408
|
my $class = shift; |
836
|
107
|
|
|
|
|
482
|
$class->save_changed; |
837
|
|
|
|
|
|
|
} |
838
|
|
|
|
|
|
|
|
839
|
|
|
|
|
|
|
sub new { |
840
|
100
|
|
|
100
|
0
|
1594
|
my $class = shift; |
841
|
100
|
|
|
|
|
360
|
my %args = @_; |
842
|
100
|
|
|
|
|
207
|
my $self = \%args; |
843
|
100
|
|
|
|
|
279
|
$self->{changed_cols} = {}; |
844
|
100
|
|
|
|
|
315
|
bless $self, $class; |
845
|
100
|
|
|
|
|
476
|
$self; |
846
|
|
|
|
|
|
|
} |
847
|
|
|
|
|
|
|
|
848
|
|
|
|
|
|
|
sub flush { |
849
|
2
|
|
|
2
|
1
|
451
|
my $self = shift; |
850
|
2
|
50
|
|
|
|
10
|
my $attr = shift or return; |
851
|
|
|
|
|
|
|
# warn "flush " . $self->object_id . '->' . $attr; |
852
|
2
|
|
|
|
|
6
|
$self->{$attr} = undef; |
853
|
2
|
|
|
|
|
14
|
$self->store_self_cache($self); |
854
|
|
|
|
|
|
|
} |
855
|
|
|
|
|
|
|
|
856
|
|
|
|
|
|
|
sub param { |
857
|
128
|
|
|
128
|
1
|
2122
|
my $self = shift; |
858
|
128
|
100
|
|
|
|
443
|
my $class = ref $self or return; |
859
|
127
|
50
|
|
|
|
369
|
return unless(defined $_[0]); |
860
|
|
|
|
|
|
|
# if (defined $_[1]) { |
861
|
127
|
100
|
|
|
|
348
|
if (@_ > 1) { |
862
|
14
|
50
|
|
|
|
147
|
@_ % 2 and croak "You gave me an odd number of parameters to param()!"; |
863
|
14
|
|
|
|
|
56
|
my %args = @_; |
864
|
14
|
|
|
|
|
192
|
$class->call_trigger('before_update', $self, \%args); |
865
|
14
|
|
|
|
|
420
|
$self->{$_} = $args{$_} for (keys %args); |
866
|
14
|
100
|
|
|
|
64
|
if ($class->is_in_session) { |
|
|
50
|
|
|
|
|
|
867
|
2
|
|
|
|
|
7
|
$self->{to_be_updated}++; |
868
|
2
|
|
|
|
|
12
|
$self->{changed_cols}->{$_}++ for (keys %args); |
869
|
2
|
|
|
|
|
5
|
push @{$class->session->{changed_objects}}, $self; |
|
2
|
|
|
|
|
27
|
|
870
|
|
|
|
|
|
|
} elsif ($class->save_explicitly) { |
871
|
0
|
|
|
|
|
0
|
$self->{to_be_updated}++; |
872
|
0
|
|
|
|
|
0
|
$self->{changed_cols}->{$_}++ for keys %args; |
873
|
|
|
|
|
|
|
} else { |
874
|
12
|
50
|
|
|
|
168
|
my $where = $self->primary_keys_hash or return; |
875
|
12
|
50
|
|
|
|
54
|
%$where or return; |
876
|
12
|
50
|
|
|
|
124
|
$class->db->update($class->table,\%args,$where) or croak 'couldnt update'; |
877
|
|
|
|
|
|
|
} |
878
|
14
|
|
|
|
|
281
|
$class->call_trigger('after_update', $self); |
879
|
|
|
|
|
|
|
# return 1; |
880
|
|
|
|
|
|
|
} |
881
|
127
|
|
|
|
|
1531
|
return $self->{$_[0]}; |
882
|
|
|
|
|
|
|
} |
883
|
|
|
|
|
|
|
|
884
|
|
|
|
|
|
|
sub primary_keys_hash { |
885
|
15
|
|
|
15
|
0
|
45
|
my $self = shift; |
886
|
15
|
50
|
|
|
|
63
|
my $class = ref $self or return; |
887
|
15
|
50
|
|
|
|
26
|
@{$class->primary_keys} or return; |
|
15
|
|
|
|
|
60
|
|
888
|
15
|
|
|
|
|
76
|
my $hash = {}; |
889
|
15
|
|
|
|
|
42
|
for (@{$class->primary_keys}) { |
|
15
|
|
|
|
|
45
|
|
890
|
15
|
50
|
|
|
|
166
|
defined $self->{$_} or return; |
891
|
15
|
|
|
|
|
66
|
$hash->{$_} = $self->{$_}; |
892
|
|
|
|
|
|
|
} |
893
|
15
|
|
|
|
|
82
|
return $hash; |
894
|
|
|
|
|
|
|
} |
895
|
|
|
|
|
|
|
|
896
|
|
|
|
|
|
|
sub set { |
897
|
10
|
|
|
10
|
1
|
1460
|
my $self = shift; |
898
|
10
|
|
|
|
|
29
|
my ($k,$v) = @_; |
899
|
10
|
|
|
|
|
102
|
$self->{$k} = $v; |
900
|
|
|
|
|
|
|
} |
901
|
|
|
|
|
|
|
|
902
|
|
|
|
|
|
|
sub has_primary_keys { |
903
|
10
|
|
|
10
|
0
|
22
|
my $self = shift; |
904
|
10
|
|
|
|
|
24
|
my $class = ref $self; |
905
|
10
|
|
|
|
|
19
|
for (@{$class->primary_keys}) { |
|
10
|
|
|
|
|
50
|
|
906
|
10
|
50
|
|
|
|
74
|
defined $self->{$_} or return; |
907
|
|
|
|
|
|
|
} |
908
|
10
|
|
|
|
|
43
|
return 1; |
909
|
|
|
|
|
|
|
} |
910
|
|
|
|
|
|
|
|
911
|
|
|
|
|
|
|
sub save { |
912
|
8
|
|
|
8
|
1
|
15
|
my $self = shift; |
913
|
8
|
|
|
|
|
13
|
my $class = ref $self; |
914
|
8
|
100
|
|
|
|
15
|
keys %{$self->{changed_cols}} or return; |
|
8
|
|
|
|
|
771
|
|
915
|
2
|
|
|
|
|
4
|
my %args; |
916
|
2
|
|
|
|
|
4
|
for (keys %{$self->{changed_cols}}) { |
|
2
|
|
|
|
|
8
|
|
917
|
|
|
|
|
|
|
# defined $self->{$_} or croak "$_ is undefined"; |
918
|
2
|
50
|
|
|
|
9
|
exists $self->{$_} or croak "$_ is undefined"; |
919
|
2
|
|
|
|
|
8
|
$args{$_} = $self->{$_}; |
920
|
|
|
|
|
|
|
} |
921
|
2
|
50
|
|
|
|
21
|
if ($self->{to_be_inserted}) { |
|
|
50
|
|
|
|
|
|
922
|
0
|
|
|
|
|
0
|
$class->db->insert($class->table,\%args); |
923
|
0
|
|
|
|
|
0
|
$self->{changed_cols} = {}; |
924
|
0
|
|
|
|
|
0
|
$self->{to_be_inserted} = undef; |
925
|
|
|
|
|
|
|
} elsif ($self->{to_be_updated}) { |
926
|
2
|
50
|
|
|
|
16
|
my $where = $self->primary_keys_hash or return; |
927
|
2
|
50
|
|
|
|
7
|
%$where or return; |
928
|
2
|
|
|
|
|
8
|
$class->db->update($class->table,\%args,$where); |
929
|
2
|
|
|
|
|
20
|
$self->{changed_cols} = {}; |
930
|
2
|
|
|
|
|
30
|
$self->{to_be_updated} = undef; |
931
|
|
|
|
|
|
|
} |
932
|
|
|
|
|
|
|
} |
933
|
|
|
|
|
|
|
|
934
|
|
|
|
|
|
|
sub object_ids { # returns all possible oids |
935
|
97
|
|
|
97
|
0
|
192
|
my $self = shift; |
936
|
97
|
50
|
|
|
|
378
|
my $class = ref $self or return; |
937
|
97
|
|
|
|
|
223
|
my $oids = {}; |
938
|
97
|
50
|
|
|
|
408
|
$oids->{$self->object_id} = 1 if $self->object_id; |
939
|
97
|
|
|
|
|
486
|
for my $key (@{$class->unique_keys}) { |
|
97
|
|
|
|
|
866
|
|
940
|
162
|
100
|
|
|
|
1920
|
next unless defined $self->{$key}; |
941
|
160
|
100
|
|
|
|
742
|
my $oid = $class->object_id($key => $self->{$key}) or next; |
942
|
136
|
|
|
|
|
696
|
$oids->{$oid}++; |
943
|
|
|
|
|
|
|
} |
944
|
97
|
|
|
|
|
2013
|
return [sort keys %$oids]; |
945
|
|
|
|
|
|
|
} |
946
|
|
|
|
|
|
|
|
947
|
|
|
|
|
|
|
1; |
948
|
|
|
|
|
|
|
|
949
|
|
|
|
|
|
|
__END__ |