| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package DBIx::QuickORM::Connection::RowData; |
|
2
|
24
|
|
|
24
|
|
179
|
use strict; |
|
|
24
|
|
|
|
|
71
|
|
|
|
24
|
|
|
|
|
1151
|
|
|
3
|
24
|
|
|
24
|
|
140
|
use warnings; |
|
|
24
|
|
|
|
|
226
|
|
|
|
24
|
|
|
|
|
2361
|
|
|
4
|
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
our $VERSION = '0.000019'; |
|
6
|
|
|
|
|
|
|
|
|
7
|
24
|
|
|
24
|
|
177
|
use Carp qw/confess croak carp/; |
|
|
24
|
|
|
|
|
76
|
|
|
|
24
|
|
|
|
|
1997
|
|
|
8
|
24
|
|
|
24
|
|
187
|
use List::Util qw/first/; |
|
|
24
|
|
|
|
|
63
|
|
|
|
24
|
|
|
|
|
1996
|
|
|
9
|
24
|
|
|
24
|
|
156
|
use Scalar::Util qw/reftype blessed/; |
|
|
24
|
|
|
|
|
73
|
|
|
|
24
|
|
|
|
|
1708
|
|
|
10
|
|
|
|
|
|
|
|
|
11
|
24
|
|
|
24
|
|
192
|
use constant STORED => 'stored'; |
|
|
24
|
|
|
|
|
50
|
|
|
|
24
|
|
|
|
|
2676
|
|
|
12
|
24
|
|
|
24
|
|
346
|
use constant PENDING => 'pending'; |
|
|
24
|
|
|
|
|
64
|
|
|
|
24
|
|
|
|
|
5683
|
|
|
13
|
24
|
|
|
24
|
|
148
|
use constant DESYNC => 'desync'; |
|
|
24
|
|
|
|
|
56
|
|
|
|
24
|
|
|
|
|
4967
|
|
|
14
|
24
|
|
|
24
|
|
162
|
use constant TRANSACTION => 'transaction'; |
|
|
24
|
|
|
|
|
67
|
|
|
|
24
|
|
|
|
|
11732
|
|
|
15
|
24
|
|
|
24
|
|
197
|
use constant ROW_DATA => 'row_data'; |
|
|
24
|
|
|
|
|
62
|
|
|
|
24
|
|
|
|
|
1620
|
|
|
16
|
|
|
|
|
|
|
|
|
17
|
24
|
|
|
24
|
|
192
|
use Importer Importer => 'import'; |
|
|
24
|
|
|
|
|
55
|
|
|
|
24
|
|
|
|
|
221
|
|
|
18
|
|
|
|
|
|
|
our @EXPORT_OK = qw{ |
|
19
|
|
|
|
|
|
|
STORED |
|
20
|
|
|
|
|
|
|
PENDING |
|
21
|
|
|
|
|
|
|
DESYNC |
|
22
|
|
|
|
|
|
|
TRANSACTION |
|
23
|
|
|
|
|
|
|
ROW_DATA |
|
24
|
|
|
|
|
|
|
}; |
|
25
|
|
|
|
|
|
|
|
|
26
|
24
|
|
|
|
|
303
|
use DBIx::QuickORM::Util::HashBase qw{ |
|
27
|
|
|
|
|
|
|
+connection |
|
28
|
|
|
|
|
|
|
+source |
|
29
|
|
|
|
|
|
|
+stack |
|
30
|
|
|
|
|
|
|
+invalid |
|
31
|
24
|
|
|
24
|
|
2685
|
}; |
|
|
24
|
|
|
|
|
66
|
|
|
32
|
|
|
|
|
|
|
|
|
33
|
17
|
100
|
|
17
|
0
|
80
|
sub valid { $_[0]->active(no_fatal => 1) ? 1 : 0 } |
|
34
|
0
|
0
|
0
|
0
|
0
|
0
|
sub invalid { $_[0]->active(no_fatal => 1) ? 0 : ($_[0]->{+INVALID} //= 'Unknown') } |
|
35
|
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
sub invalidate { |
|
37
|
0
|
|
|
0
|
0
|
0
|
my $self = shift; |
|
38
|
0
|
|
|
|
|
0
|
my %params = @_; |
|
39
|
|
|
|
|
|
|
|
|
40
|
0
|
|
|
|
|
0
|
my $reason = $params{reason}; |
|
41
|
0
|
0
|
|
|
|
0
|
unless ($reason) { |
|
42
|
0
|
|
|
|
|
0
|
my @caller = caller; |
|
43
|
0
|
|
|
|
|
0
|
$reason = "unkown at $caller[1] line $caller[2]"; |
|
44
|
|
|
|
|
|
|
} |
|
45
|
|
|
|
|
|
|
|
|
46
|
0
|
|
|
|
|
0
|
$self->{+INVALID} = $reason; |
|
47
|
|
|
|
|
|
|
|
|
48
|
0
|
|
|
|
|
0
|
my @old_stack = @{$self->{+STACK}}; |
|
|
0
|
|
|
|
|
0
|
|
|
49
|
|
|
|
|
|
|
|
|
50
|
0
|
0
|
0
|
|
|
0
|
my $active = $self->active(no_fatal => 1) // @old_stack ? $old_stack[-1] : undef; |
|
51
|
|
|
|
|
|
|
|
|
52
|
0
|
0
|
|
|
|
0
|
my $pending = $active ? $active->{+PENDING} : undef; |
|
53
|
|
|
|
|
|
|
|
|
54
|
0
|
0
|
0
|
|
|
0
|
carp "Row invalidated with pending data" if $pending && keys %$pending; |
|
55
|
|
|
|
|
|
|
|
|
56
|
0
|
|
|
|
|
0
|
$self->{+STACK} = []; |
|
57
|
|
|
|
|
|
|
} |
|
58
|
|
|
|
|
|
|
|
|
59
|
476
|
|
|
476
|
0
|
2297
|
sub source { $_[0]->{+SOURCE}->() } |
|
60
|
94
|
|
|
94
|
0
|
304
|
sub connection { $_[0]->{+CONNECTION}->() } |
|
61
|
|
|
|
|
|
|
|
|
62
|
0
|
|
|
0
|
0
|
0
|
sub stored_data { $_[0]->active->{+STORED} } |
|
63
|
2
|
|
|
2
|
0
|
11
|
sub pending_data { $_[0]->active->{+PENDING} } |
|
64
|
0
|
|
|
0
|
0
|
0
|
sub desync_data { $_[0]->active->{+DESYNC} } |
|
65
|
0
|
|
|
0
|
0
|
0
|
sub transaction { $_[0]->active->{+TRANSACTION} } |
|
66
|
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
sub init { |
|
68
|
111
|
|
|
111
|
0
|
307
|
my $self = shift; |
|
69
|
|
|
|
|
|
|
|
|
70
|
111
|
50
|
|
|
|
590
|
my $src = $self->{+SOURCE} or confess "'source' is required"; |
|
71
|
111
|
50
|
|
|
|
474
|
my $con = $self->{+CONNECTION} or confess "'connection' is required"; |
|
72
|
|
|
|
|
|
|
|
|
73
|
111
|
|
|
|
|
293
|
my ($src_sub, $src_obj); |
|
74
|
111
|
50
|
50
|
|
|
651
|
if ((reftype($src) // '') eq 'CODE') { |
|
75
|
0
|
|
|
|
|
0
|
$src_sub = $src; |
|
76
|
0
|
|
|
|
|
0
|
$src_obj = $src_sub->(); |
|
77
|
|
|
|
|
|
|
} |
|
78
|
|
|
|
|
|
|
else { |
|
79
|
111
|
|
|
|
|
250
|
$src_obj = $src; |
|
80
|
111
|
|
|
496
|
|
639
|
$src_sub = sub { $src_obj }; |
|
|
496
|
|
|
|
|
3780
|
|
|
81
|
|
|
|
|
|
|
} |
|
82
|
|
|
|
|
|
|
|
|
83
|
111
|
50
|
33
|
|
|
1212
|
croak "'source' must be either a blessed object that consumes the role 'DBIx::QuickORM::Role::Source', or a coderef that returns such an object" |
|
|
|
|
33
|
|
|
|
|
|
84
|
|
|
|
|
|
|
unless $src_obj && blessed($src_obj) && $src_obj->DOES('DBIx::QuickORM::Role::Source'); |
|
85
|
|
|
|
|
|
|
|
|
86
|
111
|
|
|
|
|
2644
|
my ($con_sub, $con_obj); |
|
87
|
111
|
50
|
50
|
|
|
620
|
if ((reftype($con) // '') eq 'CODE') { |
|
88
|
0
|
|
|
|
|
0
|
$con_sub = $con; |
|
89
|
0
|
|
|
|
|
0
|
$con_obj = $con_sub->(); |
|
90
|
|
|
|
|
|
|
} |
|
91
|
|
|
|
|
|
|
else { |
|
92
|
111
|
|
|
|
|
313
|
$con_obj = $con; |
|
93
|
111
|
|
|
99
|
|
491
|
$con_sub = sub { $con_obj }; |
|
|
99
|
|
|
|
|
537
|
|
|
94
|
|
|
|
|
|
|
} |
|
95
|
|
|
|
|
|
|
|
|
96
|
111
|
50
|
33
|
|
|
1286
|
croak "'connection' must be either a blessed instance of 'DBIx::QuickORM::Connection', or a coderef that returns such an object" |
|
|
|
|
33
|
|
|
|
|
|
97
|
|
|
|
|
|
|
unless $con_obj && blessed($con_obj) && $con_obj->isa('DBIx::QuickORM::Connection'); |
|
98
|
|
|
|
|
|
|
|
|
99
|
111
|
|
|
|
|
324
|
$self->{+CONNECTION} = $con_sub; |
|
100
|
111
|
|
|
|
|
271
|
$self->{+SOURCE} = $src_sub; |
|
101
|
111
|
|
50
|
|
|
666
|
$self->{+STACK} //= []; |
|
102
|
|
|
|
|
|
|
} |
|
103
|
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
sub active { |
|
105
|
467
|
|
|
467
|
0
|
995
|
my $self = shift; |
|
106
|
467
|
|
|
|
|
1240
|
my %params = @_; |
|
107
|
|
|
|
|
|
|
|
|
108
|
467
|
|
|
|
|
807
|
my $connection; |
|
109
|
467
|
|
|
|
|
1101
|
my $stack = $self->{+STACK}; |
|
110
|
467
|
|
|
|
|
24084
|
while (@$stack) { |
|
111
|
465
|
100
|
|
|
|
1884
|
my $txn = $stack->[0]->{+TRANSACTION} or last; # No txn, bottom state |
|
112
|
53
|
|
100
|
|
|
196
|
my $res = $txn->result // last; # Undef means still open |
|
113
|
11
|
|
|
|
|
25
|
my $done = shift @$stack; |
|
114
|
|
|
|
|
|
|
|
|
115
|
11
|
100
|
|
|
|
45
|
next unless $res; # Rolled back |
|
116
|
|
|
|
|
|
|
|
|
117
|
7
|
50
|
|
|
|
24
|
if (@$stack) { |
|
118
|
0
|
|
|
|
|
0
|
$self->_merge_state($done); |
|
119
|
|
|
|
|
|
|
} |
|
120
|
|
|
|
|
|
|
else { |
|
121
|
7
|
|
33
|
|
|
42
|
$connection //= $self->connection; |
|
122
|
7
|
50
|
|
4
|
|
40
|
$done->{+TRANSACTION} = first { !defined($_->result) && $_->id < $txn->id } reverse @{$connection->transactions}; |
|
|
4
|
|
|
|
|
30
|
|
|
|
7
|
|
|
|
|
66
|
|
|
123
|
7
|
|
|
|
|
57
|
push @$stack => $done; |
|
124
|
|
|
|
|
|
|
} |
|
125
|
|
|
|
|
|
|
} |
|
126
|
|
|
|
|
|
|
|
|
127
|
467
|
100
|
|
|
|
8369
|
return $stack->[0] if @$stack; |
|
128
|
|
|
|
|
|
|
|
|
129
|
13
|
|
100
|
|
|
47
|
$self->{+INVALID} //= "Likely inserted during a transaction that was rolled back"; |
|
130
|
|
|
|
|
|
|
|
|
131
|
13
|
100
|
|
|
|
81
|
return if $params{no_fatal}; |
|
132
|
2
|
|
|
|
|
1318
|
confess "This row is invalid (Reason: $self->{+INVALID})"; |
|
133
|
|
|
|
|
|
|
} |
|
134
|
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
sub change_state { |
|
136
|
65
|
|
|
65
|
0
|
154
|
my $self = shift; |
|
137
|
65
|
|
|
|
|
204
|
my ($state) = @_; |
|
138
|
|
|
|
|
|
|
|
|
139
|
65
|
100
|
|
|
|
313
|
my $active = $self->active(no_fatal => 1) or return $self->_up_state($state); |
|
140
|
|
|
|
|
|
|
|
|
141
|
64
|
|
|
|
|
185
|
my $row_txn = $self->active->{+TRANSACTION}; |
|
142
|
64
|
|
|
|
|
189
|
my $state_txn = $state->{+TRANSACTION}; |
|
143
|
|
|
|
|
|
|
|
|
144
|
64
|
100
|
|
|
|
204
|
my $state_res = $state_txn ? $state_txn->result : undef; |
|
145
|
64
|
100
|
|
|
|
180
|
my $row_res = $row_txn ? $row_txn->result : undef; |
|
146
|
|
|
|
|
|
|
|
|
147
|
64
|
50
|
33
|
|
|
262
|
croak "Refusing to merge down a rolled-back transaction" if defined($state_res) && !$state_res; |
|
148
|
|
|
|
|
|
|
|
|
149
|
64
|
|
|
|
|
135
|
my $merge = 0; |
|
150
|
64
|
|
66
|
|
|
600
|
$merge ||= !($row_txn || $state_txn); |
|
|
|
|
66
|
|
|
|
|
|
151
|
64
|
|
66
|
|
|
224
|
$merge ||= $state_txn == $row_txn; |
|
152
|
|
|
|
|
|
|
|
|
153
|
64
|
50
|
|
|
|
225
|
if ($merge) { |
|
154
|
|
|
|
|
|
|
# If the transactions are the same, or if there are no txns for eather, just merge. |
|
155
|
64
|
|
|
|
|
320
|
$self->_merge_state($state); |
|
156
|
|
|
|
|
|
|
} |
|
157
|
|
|
|
|
|
|
else { |
|
158
|
0
|
|
|
|
|
0
|
$self->_up_state($state); |
|
159
|
|
|
|
|
|
|
} |
|
160
|
|
|
|
|
|
|
|
|
161
|
64
|
|
|
|
|
249
|
return $self; |
|
162
|
|
|
|
|
|
|
} |
|
163
|
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
sub _up_state { |
|
165
|
1
|
|
|
1
|
|
3
|
my $self = shift; |
|
166
|
1
|
|
|
|
|
3
|
my ($state) = @_; |
|
167
|
|
|
|
|
|
|
|
|
168
|
1
|
|
|
|
|
3
|
my $stack = $self->{+STACK}; |
|
169
|
1
|
50
|
33
|
|
|
4
|
croak "There is already a base state, and no txn was provided" if @$stack && !$state->{+TRANSACTION}; |
|
170
|
1
|
|
|
|
|
4
|
unshift @$stack => $state; |
|
171
|
1
|
|
|
|
|
3
|
return $self; |
|
172
|
|
|
|
|
|
|
} |
|
173
|
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
sub _merge_state { |
|
175
|
64
|
|
|
64
|
|
135
|
my $self = shift; |
|
176
|
64
|
|
|
|
|
181
|
my ($merge, $source, $connection) = @_; |
|
177
|
|
|
|
|
|
|
|
|
178
|
64
|
|
|
|
|
197
|
my $into = $self->active; |
|
179
|
|
|
|
|
|
|
|
|
180
|
64
|
100
|
|
|
|
3422
|
if (my $stored = $merge->{+STORED}) { |
|
|
|
50
|
|
|
|
|
|
|
181
|
56
|
100
|
|
|
|
241
|
if (my $pending = $into->{+PENDING}) { |
|
182
|
13
|
|
|
|
|
31
|
for my $field (keys %{$merge->{+STORED}}) { |
|
|
13
|
|
|
|
|
67
|
|
|
183
|
37
|
|
66
|
|
|
184
|
$source //= $self->source; |
|
184
|
37
|
|
66
|
|
|
164
|
$connection //= $self->connection; |
|
185
|
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
# No change |
|
187
|
37
|
100
|
|
|
|
143
|
next if $self->compare_field($field, $into->{+STORED}, $stored, $source, $connection); |
|
188
|
|
|
|
|
|
|
|
|
189
|
16
|
|
|
|
|
67
|
$into->{+STORED}->{$field} = $stored->{$field}; |
|
190
|
16
|
100
|
|
|
|
116
|
$into->{+DESYNC}->{$field} = 1 if $pending->{$field}; |
|
191
|
|
|
|
|
|
|
} |
|
192
|
13
|
50
|
|
|
|
72
|
$into->{+PENDING} = $pending if keys %$pending; |
|
193
|
|
|
|
|
|
|
} |
|
194
|
|
|
|
|
|
|
else { |
|
195
|
43
|
|
|
|
|
100
|
delete $into->{+DESYNC}; |
|
196
|
43
|
50
|
50
|
|
|
132
|
$into->{+STORED} = $into->{+STORED} ? {%{$into->{+STORED} // {}}, %{$stored}} : $stored; |
|
|
43
|
|
|
|
|
260
|
|
|
|
43
|
|
|
|
|
318
|
|
|
197
|
|
|
|
|
|
|
} |
|
198
|
|
|
|
|
|
|
} |
|
199
|
|
|
|
|
|
|
elsif (exists $merge->{+STORED}) { |
|
200
|
8
|
|
|
|
|
36
|
delete $into->{+STORED}; |
|
201
|
8
|
|
|
|
|
39
|
delete $into->{+DESYNC}; |
|
202
|
8
|
|
|
|
|
16
|
delete $merge->{+DESYNC}; |
|
203
|
|
|
|
|
|
|
} |
|
204
|
|
|
|
|
|
|
|
|
205
|
64
|
100
|
66
|
|
|
437
|
delete $into->{+DESYNC} if exists $merge->{+DESYNC} && !$merge->{+DESYNC}; |
|
206
|
|
|
|
|
|
|
|
|
207
|
64
|
|
|
|
|
199
|
my $desync = $merge->{+DESYNC}; |
|
208
|
64
|
50
|
|
|
|
307
|
if (my $pending = $merge->{+PENDING}) { |
|
|
|
100
|
|
|
|
|
|
|
209
|
0
|
0
|
|
|
|
0
|
$into->{+PENDING} = $into->{+PENDING} ? {%{$self->{+PENDING}}, %$pending} : $pending; |
|
|
0
|
|
|
|
|
0
|
|
|
210
|
0
|
0
|
|
|
|
0
|
$into->{+DESYNC} = $into->{+DESYNC} ? {%{$self->{+DESYNC}}, %$desync} : $desync if $desync; |
|
|
0
|
0
|
|
|
|
0
|
|
|
211
|
|
|
|
|
|
|
} |
|
212
|
|
|
|
|
|
|
elsif (exists $merge->{+PENDING}) { |
|
213
|
15
|
|
|
|
|
62
|
delete $into->{+PENDING}; |
|
214
|
|
|
|
|
|
|
} |
|
215
|
|
|
|
|
|
|
|
|
216
|
64
|
50
|
66
|
|
|
307
|
delete $into->{+PENDING} if $into->{+PENDING} && !keys %{$into->{+PENDING}}; |
|
|
1
|
|
|
|
|
7
|
|
|
217
|
64
|
100
|
|
|
|
240
|
delete $into->{+DESYNC} unless $into->{+PENDING}; |
|
218
|
|
|
|
|
|
|
|
|
219
|
64
|
|
|
|
|
216
|
return $self; |
|
220
|
|
|
|
|
|
|
} |
|
221
|
|
|
|
|
|
|
|
|
222
|
|
|
|
|
|
|
sub compare_field { |
|
223
|
37
|
|
|
37
|
0
|
74
|
my $self = shift; |
|
224
|
37
|
|
|
|
|
108
|
my ($field, $ah, $bh, $source, $connection) = @_; |
|
225
|
|
|
|
|
|
|
|
|
226
|
37
|
|
33
|
|
|
102
|
$source //= $self->source; |
|
227
|
37
|
|
33
|
|
|
88
|
$connection //= $self->connection; |
|
228
|
|
|
|
|
|
|
|
|
229
|
37
|
|
|
|
|
217
|
my $affinity = $source->field_affinity($field, $connection->dialect); |
|
230
|
37
|
|
|
|
|
131
|
my $type = $source->field_type($field); |
|
231
|
|
|
|
|
|
|
|
|
232
|
37
|
|
|
|
|
330
|
my $ae = exists $ah->{$field}; |
|
233
|
37
|
|
|
|
|
74
|
my $be = exists $bh->{$field}; |
|
234
|
37
|
100
|
50
|
|
|
228
|
return 0 if ($ae xor $be); # One exists, one does not |
|
235
|
32
|
0
|
33
|
|
|
85
|
return 1 if (!$ae) && (!$be); # Neither exists |
|
236
|
|
|
|
|
|
|
|
|
237
|
32
|
|
|
|
|
84
|
my $a = $ah->{$field}; |
|
238
|
32
|
|
|
|
|
63
|
my $b = $bh->{$field}; |
|
239
|
|
|
|
|
|
|
|
|
240
|
32
|
|
|
|
|
66
|
my $ad = defined($a); |
|
241
|
32
|
|
|
|
|
57
|
my $bd = defined($b); |
|
242
|
32
|
50
|
25
|
|
|
143
|
return 0 if ($ad xor $bd); # One is defined, one is not |
|
243
|
32
|
0
|
33
|
|
|
90
|
return 1 if (!$ad) && (!$bd); # Neither is defined |
|
244
|
|
|
|
|
|
|
|
|
245
|
|
|
|
|
|
|
# true if different, false if same |
|
246
|
32
|
100
|
|
|
|
119
|
return !$type->qorm_compare($a, $b) if $type; |
|
247
|
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
# true if same, false if different |
|
249
|
25
|
|
|
|
|
124
|
return DBIx::QuickORM::Affinity::compare_affinity_values($affinity, $a, $b); |
|
250
|
|
|
|
|
|
|
} |
|
251
|
|
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
1; |