line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Yancy::Backend::Memory; |
2
|
|
|
|
|
|
|
our $VERSION = '1.088'; |
3
|
|
|
|
|
|
|
# ABSTRACT: A backend entirely in memory |
4
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
#pod =head1 DESCRIPTION |
6
|
|
|
|
|
|
|
#pod |
7
|
|
|
|
|
|
|
#pod An in-memory "database" backend for Yancy. Uses L to implement |
8
|
|
|
|
|
|
|
#pod basic searching for (). |
9
|
|
|
|
|
|
|
#pod |
10
|
|
|
|
|
|
|
#pod =cut |
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
# XXX: TODO Remove references to Local::Test |
13
|
|
|
|
|
|
|
|
14
|
23
|
|
|
23
|
|
20334
|
use Mojo::Base '-base'; |
|
23
|
|
|
|
|
60
|
|
|
23
|
|
|
|
|
167
|
|
15
|
23
|
|
|
23
|
|
4108
|
use List::Util qw( max ); |
|
23
|
|
|
|
|
57
|
|
|
23
|
|
|
|
|
1580
|
|
16
|
23
|
|
|
23
|
|
172
|
use Mojo::JSON qw( true false from_json to_json encode_json ); |
|
23
|
|
|
|
|
48
|
|
|
23
|
|
|
|
|
1620
|
|
17
|
23
|
|
|
23
|
|
151
|
use Mojo::File qw( path ); |
|
23
|
|
|
|
|
50
|
|
|
23
|
|
|
|
|
1313
|
|
18
|
23
|
|
|
23
|
|
176
|
use Storable qw( dclone ); |
|
23
|
|
|
|
|
49
|
|
|
23
|
|
|
|
|
1198
|
|
19
|
23
|
|
|
23
|
|
147
|
use Role::Tiny qw( with ); |
|
23
|
|
|
|
|
83
|
|
|
23
|
|
|
|
|
244
|
|
20
|
|
|
|
|
|
|
with 'Yancy::Backend::Role::Sync'; |
21
|
23
|
|
|
23
|
|
5268
|
use Yancy::Util qw( match is_type order_by is_format ); |
|
23
|
|
|
|
|
51
|
|
|
23
|
|
|
|
|
1700
|
|
22
|
23
|
|
|
23
|
|
13845
|
use Time::Piece; |
|
23
|
|
|
|
|
170943
|
|
|
23
|
|
|
|
|
115
|
|
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
our %DATA; |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
sub new { |
27
|
85
|
|
|
85
|
1
|
75552
|
my ( $class, $url, $schema ) = @_; |
28
|
85
|
100
|
|
|
|
381
|
if ( $url ) { |
29
|
84
|
|
|
|
|
536
|
my ( $path ) = $url =~ m{^[^:]+://[^/]+(?:/(.+))?$}; |
30
|
84
|
50
|
|
|
|
331
|
if ( $path ) { |
31
|
0
|
|
0
|
|
|
0
|
%DATA = %{ from_json( path( ( $ENV{MOJO_HOME} || () ), $path )->slurp ) }; |
|
0
|
|
|
|
|
0
|
|
32
|
|
|
|
|
|
|
} |
33
|
|
|
|
|
|
|
} |
34
|
85
|
|
100
|
|
|
342
|
$schema //= \%Local::Test::SCHEMA; |
35
|
85
|
|
|
|
|
566
|
return bless { init_arg => $url, schema => $schema }, $class; |
36
|
|
|
|
|
|
|
} |
37
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
sub schema { |
39
|
3210
|
|
|
3210
|
0
|
9812
|
my ( $self, $schema ) = @_; |
40
|
3210
|
100
|
|
|
|
6691
|
if ( $schema ) { |
41
|
52
|
|
|
|
|
160
|
$self->{schema} = $schema; |
42
|
52
|
|
|
|
|
151
|
return; |
43
|
|
|
|
|
|
|
} |
44
|
3158
|
|
|
|
|
11287
|
$self->{schema}; |
45
|
|
|
|
|
|
|
} |
46
|
|
|
|
|
|
|
sub collections; |
47
|
|
|
|
|
|
|
*collections = *schema; |
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
sub create { |
50
|
116
|
|
|
116
|
0
|
28766
|
my ( $self, $schema_name, $params ) = @_; |
51
|
116
|
|
|
|
|
628
|
$params = { %$params }; |
52
|
116
|
|
|
|
|
412
|
my $props = $self->schema->{ $schema_name }{properties}; |
53
|
|
|
|
|
|
|
$params->{ $_ } = $props->{ $_ }{default} // undef |
54
|
116
|
|
100
|
|
|
1517
|
for grep !exists $params->{ $_ }, |
55
|
|
|
|
|
|
|
keys %$props; |
56
|
116
|
|
|
|
|
487
|
$params = $self->_normalize( $schema_name, $params ); # makes a copy |
57
|
|
|
|
|
|
|
|
58
|
116
|
|
100
|
|
|
405
|
my $id_field = $self->schema->{ $schema_name }{ 'x-id-field' } || 'id'; |
59
|
116
|
100
|
|
|
|
567
|
my @id_fields = ref $id_field eq 'ARRAY' ? @$id_field : ( $id_field ); |
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
# Fill in any auto-increment data... |
62
|
116
|
|
|
|
|
366
|
for my $id_field ( @id_fields ) { |
63
|
|
|
|
|
|
|
# We haven't provided a value for an integer ID, assume it's autoinc |
64
|
121
|
100
|
66
|
|
|
825
|
if ( !$params->{ $id_field } and $self->schema->{ $schema_name }{properties}{ $id_field }{type} eq 'integer' ) { |
|
|
100
|
66
|
|
|
|
|
|
|
|
66
|
|
|
|
|
65
|
58
|
|
|
|
|
111
|
my @existing_ids = keys %{ $DATA{ $schema_name } }; |
|
58
|
|
|
|
|
228
|
|
66
|
58
|
|
100
|
|
|
489
|
$params->{ $id_field} = ( max( @existing_ids ) // 0 ) + 1; |
67
|
|
|
|
|
|
|
} |
68
|
|
|
|
|
|
|
# We have provided another ID, make 'id' another autoinc |
69
|
|
|
|
|
|
|
elsif ( $params->{ $id_field } |
70
|
|
|
|
|
|
|
&& $id_field ne 'id' |
71
|
|
|
|
|
|
|
&& exists $self->schema->{ $schema_name }{properties}{id} |
72
|
|
|
|
|
|
|
) { |
73
|
46
|
|
|
|
|
94
|
my @existing_ids = map { $_->{ id } } values %{ $DATA{ $schema_name } }; |
|
45
|
|
|
|
|
140
|
|
|
46
|
|
|
|
|
167
|
|
74
|
46
|
|
100
|
|
|
323
|
$params->{id} = ( max( @existing_ids ) // 0 ) + 1; |
75
|
|
|
|
|
|
|
} |
76
|
|
|
|
|
|
|
} |
77
|
|
|
|
|
|
|
|
78
|
116
|
|
100
|
|
|
434
|
my $store = $DATA{ $schema_name } //= {}; |
79
|
116
|
|
|
|
|
387
|
for my $i ( 0 .. $#id_fields-1 ) { |
80
|
5
|
|
100
|
|
|
37
|
$store = $store->{ $params->{ $id_fields[$i] } } //= {}; |
81
|
|
|
|
|
|
|
} |
82
|
116
|
|
|
|
|
449
|
$store->{ $params->{ $id_fields[-1] } } = $params; |
83
|
|
|
|
|
|
|
|
84
|
116
|
100
|
|
|
|
655
|
return @id_fields > 1 ? { map {; $_ => $params->{ $_ } } @id_fields } : $params->{ $id_field }; |
|
10
|
|
|
|
|
49
|
|
85
|
|
|
|
|
|
|
} |
86
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
sub get { |
88
|
328
|
|
|
328
|
0
|
418401
|
my ( $self, $schema_name, $id, %opt ) = @_; |
89
|
328
|
|
|
|
|
935
|
my $schema = $self->schema->{ $schema_name }; |
90
|
328
|
|
100
|
|
|
2384
|
my $real_coll = ( $schema->{'x-view'} || {} )->{schema} // $schema_name; |
|
|
|
66
|
|
|
|
|
91
|
|
|
|
|
|
|
|
92
|
328
|
|
100
|
|
|
907
|
my $id_field = $self->schema->{ $schema_name }{ 'x-id-field' } || 'id'; |
93
|
328
|
100
|
|
|
|
1281
|
my @ids = ref $id_field eq 'ARRAY' ? map { $id->{ $_ } } @$id_field : ( $id ); |
|
25
|
|
|
|
|
83
|
|
94
|
327
|
100
|
66
|
|
|
1107
|
die "Missing composite ID parts" if @ids > 1 && ( !ref $id || keys %$id < @ids ); |
|
|
|
100
|
|
|
|
|
95
|
|
|
|
|
|
|
|
96
|
326
|
|
|
|
|
777
|
my $item = $DATA{ $real_coll }; |
97
|
326
|
|
|
|
|
784
|
for my $id ( @ids ) { |
98
|
337
|
100
|
|
|
|
975
|
return undef if !defined $id; |
99
|
334
|
|
100
|
|
|
1412
|
$item = $item->{ $id } // return undef; |
100
|
|
|
|
|
|
|
} |
101
|
|
|
|
|
|
|
|
102
|
281
|
|
|
|
|
975
|
$item = $self->_viewise( $schema_name, $item ); |
103
|
281
|
100
|
|
|
|
889
|
if ( my $join = $opt{join} ) { |
104
|
4
|
|
|
|
|
16
|
$item = $self->_join( $schema_name, $item, $join ); |
105
|
|
|
|
|
|
|
} |
106
|
|
|
|
|
|
|
|
107
|
281
|
|
|
|
|
1461
|
return $item; |
108
|
|
|
|
|
|
|
} |
109
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
sub _join { |
111
|
21
|
|
|
21
|
|
52
|
my ( $self, $schema_name, $item, $join, $where ) = @_; |
112
|
21
|
|
|
|
|
105
|
$item = { %$item }; |
113
|
21
|
|
|
|
|
53
|
my $schema = $self->schema->{ $schema_name }; |
114
|
21
|
|
|
|
|
34
|
my %props = %{ $schema->{properties} }; |
|
21
|
|
|
|
|
75
|
|
115
|
21
|
|
50
|
|
|
64
|
my $id_field = $schema->{ 'x-id-field' } || 'id'; |
116
|
21
|
100
|
|
|
|
60
|
my @joins = ref $join eq 'ARRAY' ? @$join : ( $join ); |
117
|
21
|
|
|
|
|
38
|
for my $join ( @joins ) { |
118
|
29
|
100
|
|
|
|
74
|
if ( my $join_prop = $schema->{ properties }{ $join } ) { |
|
|
50
|
|
|
|
|
|
119
|
21
|
|
100
|
|
|
51
|
my $join_id = $item->{ $join } || next; |
120
|
17
|
|
|
|
|
27
|
my $join_schema_name = $join_prop->{'x-foreign-key'}; |
121
|
17
|
|
|
|
|
38
|
$item->{ $join } = $self->get( $join_schema_name, $join_id ); |
122
|
17
|
|
|
|
|
125
|
for my $key ( grep /^${join}\./, keys %$where ) { |
123
|
7
|
|
|
|
|
80
|
my ( $k ) = $key =~ /^${join}\.(.+)$/; |
124
|
7
|
100
|
|
|
|
35
|
if ( !match( { $k => $where->{ $key } }, $item->{ $join } ) ) { |
125
|
|
|
|
|
|
|
# Inner match fails, so this row is not in the |
126
|
|
|
|
|
|
|
# results |
127
|
2
|
|
|
|
|
15
|
return; |
128
|
|
|
|
|
|
|
} |
129
|
|
|
|
|
|
|
} |
130
|
|
|
|
|
|
|
} |
131
|
|
|
|
|
|
|
elsif ( my $join_schema = $self->schema->{ $join } ) { |
132
|
8
|
|
|
|
|
16
|
my $join_schema_name = $join; |
133
|
8
|
|
|
|
|
19
|
my $join_props = $join_schema->{properties}; |
134
|
|
|
|
|
|
|
my $join_where = { |
135
|
2
|
|
|
|
|
22
|
map { s/^$join\.//r => $where->{ $_ } } |
136
|
8
|
|
|
|
|
24
|
grep { /^$join\./ } |
|
2
|
|
|
|
|
27
|
|
137
|
|
|
|
|
|
|
keys %$where |
138
|
|
|
|
|
|
|
}; |
139
|
|
|
|
|
|
|
|
140
|
|
|
|
|
|
|
# First try to find the foreign key on the local schema |
141
|
8
|
100
|
100
|
|
|
25
|
if ( my ( $join_prop_name ) = grep { ($props{ $_ }{ 'x-foreign-key' }//'') =~ /^$join_schema_name(\.|$)/ } keys %props ) { |
|
27
|
50
|
|
|
|
253
|
|
142
|
3
|
|
|
|
|
8
|
my $join_prop = $props{ $join_prop_name }; |
143
|
3
|
|
|
|
|
11
|
my ( undef, $join_key_field ) = split /\./, $join_prop->{'x-foreign-key'}; |
144
|
3
|
|
50
|
|
|
21
|
$join_key_field //= $join_schema->{'x-id-field'} // 'id'; |
|
|
|
33
|
|
|
|
|
145
|
|
|
|
|
|
|
# Find the one foreign item |
146
|
3
|
|
|
|
|
20
|
my $res = $self->list( $join_schema_name, { %$join_where, $join_key_field => $item->{ $join_prop_name } } ); |
147
|
3
|
50
|
33
|
|
|
15
|
return if keys %$join_where && !$res->{total}; |
148
|
3
|
|
|
|
|
14
|
$item->{ $join } = $res->{items}[0]; |
149
|
|
|
|
|
|
|
} |
150
|
|
|
|
|
|
|
# Otherwise, try to find the foreign key on the foreign schema |
151
|
20
|
|
100
|
|
|
112
|
elsif ( ( $join_prop_name ) = grep { ($join_props->{ $_ }{ 'x-foreign-key' }//'') =~ /^$schema_name(\.|$)/ } keys %$join_props ) { |
152
|
5
|
|
|
|
|
12
|
my $join_prop = $join_props->{ $join_prop_name }; |
153
|
5
|
|
|
|
|
10
|
my $join_key_field; |
154
|
5
|
50
|
|
|
|
19
|
if ( $join_prop->{'x-foreign-key'} =~ /\.(.+)$/ ) { |
155
|
0
|
|
|
|
|
0
|
$join_key_field = $1; |
156
|
|
|
|
|
|
|
} |
157
|
|
|
|
|
|
|
else { |
158
|
5
|
|
50
|
|
|
14
|
$join_key_field = $schema->{'x-id-field'} // 'id'; |
159
|
|
|
|
|
|
|
} |
160
|
|
|
|
|
|
|
# Find the list of foreign items |
161
|
5
|
|
|
|
|
33
|
my $res = $self->list( $join_schema_name, { %$join_where, $join_prop_name => $item->{ $join_key_field } } ); |
162
|
5
|
100
|
100
|
|
|
32
|
return if keys %$join_where && !$res->{total}; |
163
|
4
|
|
|
|
|
18
|
$item->{ $join } = $res->{items}; |
164
|
|
|
|
|
|
|
} |
165
|
|
|
|
|
|
|
else { |
166
|
0
|
|
|
|
|
0
|
die "Could not join $schema_name to $join: No x-foreign-key property found"; |
167
|
|
|
|
|
|
|
} |
168
|
|
|
|
|
|
|
} |
169
|
|
|
|
|
|
|
} |
170
|
18
|
|
|
|
|
68
|
return $item; |
171
|
|
|
|
|
|
|
} |
172
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
sub _viewise { |
174
|
631
|
|
|
631
|
|
2020
|
my ( $self, $schema_name, $item, $join ) = @_; |
175
|
631
|
|
|
|
|
23153
|
$item = dclone $item; |
176
|
631
|
|
|
|
|
2347
|
my $schema = $self->schema->{ $schema_name }; |
177
|
631
|
|
100
|
|
|
3972
|
my $real_coll = ( $schema->{'x-view'} || {} )->{schema} // $schema_name; |
|
|
|
66
|
|
|
|
|
178
|
|
|
|
|
|
|
my %props = %{ |
179
|
631
|
|
|
|
|
1245
|
$schema->{properties} || $self->schema->{ $real_coll }{properties} |
180
|
631
|
100
|
|
|
|
4612
|
}; |
181
|
631
|
100
|
|
|
|
1877
|
if ( $join ) { |
182
|
13
|
100
|
|
|
|
23
|
$props{ $_ } = 1 for @{ ref $join eq 'ARRAY' ? $join : [ $join ] }; |
|
13
|
|
|
|
|
53
|
|
183
|
|
|
|
|
|
|
} |
184
|
631
|
|
|
|
|
4135
|
delete $item->{$_} for grep !$props{ $_ }, keys %$item; |
185
|
631
|
|
|
|
|
3002
|
$item; |
186
|
|
|
|
|
|
|
} |
187
|
|
|
|
|
|
|
|
188
|
|
|
|
|
|
|
sub list { |
189
|
336
|
|
|
336
|
0
|
363605
|
my ( $self, $schema_name, $params, @opt ) = @_; |
190
|
336
|
100
|
|
|
|
1341
|
my $opt = @opt % 2 == 0 ? {@opt} : $opt[0]; |
191
|
336
|
|
|
|
|
1067
|
my $schema = $self->schema->{ $schema_name }; |
192
|
336
|
50
|
|
|
|
978
|
die "list attempted on non-existent schema '$schema_name'" unless $schema; |
193
|
336
|
|
100
|
|
|
1184
|
$params ||= {}; |
194
|
|
|
|
|
|
|
|
195
|
336
|
|
100
|
|
|
741
|
my $id_field = $self->schema->{ $schema_name }{ 'x-id-field' } || 'id'; |
196
|
336
|
100
|
|
|
|
1178
|
my @id_fields = ref $id_field eq 'ARRAY' ? @$id_field : ( $id_field ); |
197
|
|
|
|
|
|
|
|
198
|
336
|
|
100
|
|
|
2222
|
my $real_coll = ( $schema->{'x-view'} || {} )->{schema} // $schema_name; |
|
|
|
66
|
|
|
|
|
199
|
|
|
|
|
|
|
my $props = $schema->{properties} |
200
|
336
|
|
33
|
|
|
1185
|
|| $self->schema->{ $real_coll }{properties}; |
201
|
336
|
|
|
|
|
628
|
my @rows = values %{ $DATA{ $real_coll } }; |
|
336
|
|
|
|
|
1255
|
|
202
|
336
|
|
|
|
|
1208
|
for my $id_field ( 1..$#id_fields ) { |
203
|
9
|
|
|
|
|
89
|
@rows = map values %$_, @rows; |
204
|
|
|
|
|
|
|
} |
205
|
336
|
100
|
|
|
|
1065
|
if ( $opt->{join} ) { |
206
|
7
|
|
|
|
|
24
|
@rows = map $self->_join( $schema_name, $_, $opt->{join}, $params ), @rows; |
207
|
|
|
|
|
|
|
} |
208
|
|
|
|
|
|
|
# Join queries have been resolved |
209
|
336
|
100
|
|
|
|
1121
|
for my $p ( ref $params eq 'ARRAY' ? @$params : ( $params ) ) { |
210
|
337
|
|
|
|
|
1394
|
for my $key ( grep /\./, keys %$p ) { |
211
|
4
|
|
|
|
|
10
|
delete $p->{ $key }; |
212
|
4
|
|
|
|
|
14
|
my ( $j ) = split /\./, $key; |
213
|
4
|
|
|
|
|
15
|
$p->{ $j } = { '!=' => undef }; |
214
|
|
|
|
|
|
|
} |
215
|
|
|
|
|
|
|
} |
216
|
|
|
|
|
|
|
my $matched_rows = order_by( |
217
|
|
|
|
|
|
|
$opt->{order_by} // \@id_fields, |
218
|
336
|
|
100
|
|
|
1747
|
[ grep { match( $params, $_ ) } @rows ], |
|
541
|
|
|
|
|
1521
|
|
219
|
|
|
|
|
|
|
); |
220
|
336
|
|
100
|
|
|
1447
|
my $first = $opt->{offset} // 0; |
221
|
336
|
100
|
|
|
|
1018
|
my $last = $opt->{limit} ? $opt->{limit} + $first - 1 : $#$matched_rows; |
222
|
336
|
100
|
|
|
|
929
|
if ( $last > $#$matched_rows ) { |
223
|
58
|
|
|
|
|
114
|
$last = $#$matched_rows; |
224
|
|
|
|
|
|
|
} |
225
|
336
|
|
|
|
|
1656
|
my @items = map $self->_viewise( $schema_name, $_, $opt->{join} ), @$matched_rows[ $first .. $last ]; |
226
|
336
|
|
|
|
|
1355
|
my $retval = { |
227
|
|
|
|
|
|
|
items => \@items, |
228
|
|
|
|
|
|
|
total => scalar @$matched_rows, |
229
|
|
|
|
|
|
|
}; |
230
|
|
|
|
|
|
|
#; use Data::Dumper; |
231
|
|
|
|
|
|
|
#; say Dumper $retval; |
232
|
336
|
|
|
|
|
1784
|
return $retval; |
233
|
|
|
|
|
|
|
} |
234
|
|
|
|
|
|
|
|
235
|
|
|
|
|
|
|
sub set { |
236
|
58
|
|
|
58
|
0
|
46962
|
my ( $self, $schema_name, $id, $params ) = @_; |
237
|
58
|
|
100
|
|
|
192
|
my $id_field = $self->schema->{ $schema_name }{ 'x-id-field' } || 'id'; |
238
|
58
|
100
|
|
|
|
269
|
my @id_fields = ref $id_field eq 'ARRAY' ? @$id_field : ( $id_field ); |
239
|
58
|
100
|
100
|
|
|
342
|
die "Missing composite ID parts" if @id_fields > 1 && ( !ref $id || keys %$id < @id_fields ); |
|
|
|
100
|
|
|
|
|
240
|
|
|
|
|
|
|
|
241
|
|
|
|
|
|
|
# Fill in any missing params from the ID |
242
|
56
|
|
|
|
|
166
|
for my $id_field ( @id_fields ) { |
243
|
58
|
100
|
|
|
|
202
|
my $id_part = ref $id eq 'HASH' ? $id->{ $id_field } : $id; |
244
|
58
|
100
|
|
|
|
241
|
if ( !$params->{ $id_field } ) { |
245
|
34
|
|
|
|
|
154
|
$params->{ $id_field } = $id_part; |
246
|
|
|
|
|
|
|
} |
247
|
|
|
|
|
|
|
} |
248
|
|
|
|
|
|
|
|
249
|
56
|
|
|
|
|
232
|
$params = $self->_normalize( $schema_name, $params ); |
250
|
|
|
|
|
|
|
|
251
|
56
|
|
|
|
|
183
|
my $store = $DATA{ $schema_name }; |
252
|
56
|
|
|
|
|
249
|
for my $i ( 0..$#id_fields-1 ) { |
253
|
2
|
|
|
|
|
9
|
my $id_field = $id_fields[ $i ]; |
254
|
2
|
50
|
|
|
|
18
|
my $id_part = ref $id eq 'HASH' ? $id->{ $id_field } : $id; |
255
|
2
|
50
|
|
|
|
11
|
return 0 if !$store->{ $id_part }; |
256
|
|
|
|
|
|
|
# Update the item's ID if it changes |
257
|
2
|
|
|
|
|
6
|
my $item = delete $store->{ $id_part }; |
258
|
2
|
|
|
|
|
7
|
$store->{ $params->{ $id_field } } = $item; |
259
|
2
|
|
|
|
|
9
|
$store = $item; |
260
|
|
|
|
|
|
|
} |
261
|
56
|
100
|
|
|
|
237
|
my $id_part = ref $id eq 'HASH' ? $id->{ $id_fields[-1] } : $id; |
262
|
56
|
100
|
|
|
|
240
|
return 0 if !$store->{ $id_part }; |
263
|
|
|
|
|
|
|
$store->{ $params->{ $id_fields[-1] } } = { |
264
|
46
|
|
|
|
|
103
|
%{ delete $store->{ $id_part } }, |
|
46
|
|
|
|
|
609
|
|
265
|
|
|
|
|
|
|
%$params, |
266
|
|
|
|
|
|
|
}; |
267
|
|
|
|
|
|
|
|
268
|
46
|
|
|
|
|
368
|
return 1; |
269
|
|
|
|
|
|
|
} |
270
|
|
|
|
|
|
|
|
271
|
|
|
|
|
|
|
sub delete { |
272
|
94
|
|
|
94
|
0
|
968424
|
my ( $self, $schema_name, $id ) = @_; |
273
|
94
|
50
|
|
|
|
356
|
return 0 if !$id; |
274
|
94
|
|
100
|
|
|
252
|
my $id_field = $self->schema->{ $schema_name }{ 'x-id-field' } || 'id'; |
275
|
94
|
100
|
|
|
|
355
|
my @id_fields = ref $id_field eq 'ARRAY' ? @$id_field : ( $id_field ); |
276
|
94
|
100
|
100
|
|
|
321
|
die "Missing composite ID parts" if @id_fields > 1 && ( !ref $id || keys %$id < @id_fields ); |
|
|
|
100
|
|
|
|
|
277
|
92
|
|
|
|
|
220
|
my $store = $DATA{ $schema_name }; |
278
|
92
|
|
|
|
|
316
|
for my $i ( 0..$#id_fields-1 ) { |
279
|
2
|
|
|
|
|
7
|
my $id_field = $id_fields[ $i ]; |
280
|
2
|
50
|
|
|
|
12
|
my $id_part = ref $id eq 'HASH' ? $id->{ $id_field } : $id; |
281
|
2
|
|
50
|
|
|
13
|
$store = $store->{ $id_part } // return 0; |
282
|
|
|
|
|
|
|
} |
283
|
92
|
100
|
|
|
|
276
|
my $id_part = ref $id eq 'HASH' ? $id->{ $id_fields[-1] } : $id; |
284
|
92
|
100
|
|
|
|
377
|
return 0 if !$store->{ $id_part }; |
285
|
70
|
|
|
|
|
489
|
return !!delete $store->{ $id_part }; |
286
|
|
|
|
|
|
|
} |
287
|
|
|
|
|
|
|
|
288
|
|
|
|
|
|
|
sub _normalize { |
289
|
172
|
|
|
172
|
|
467
|
my ( $self, $schema_name, $data ) = @_; |
290
|
172
|
50
|
|
|
|
498
|
return undef if !$data; |
291
|
172
|
|
|
|
|
402
|
my $schema = $self->schema->{ $schema_name }{ properties }; |
292
|
172
|
|
|
|
|
367
|
my %replace; |
293
|
172
|
|
|
|
|
653
|
for my $key ( keys %$data ) { |
294
|
1044
|
100
|
|
|
|
10091
|
next if !defined $data->{ $key }; # leave nulls alone |
295
|
825
|
|
|
|
|
1179
|
my ( $type, $format ) = @{ $schema->{ $key } }{qw( type format )}; |
|
825
|
|
|
|
|
2078
|
|
296
|
825
|
100
|
100
|
|
|
1930
|
if ( is_type( $type, 'boolean' ) ) { |
|
|
100
|
|
|
|
|
|
297
|
|
|
|
|
|
|
# Boolean: true (1, "true"), false (0, "false") |
298
|
|
|
|
|
|
|
$replace{ $key } |
299
|
56
|
100
|
100
|
|
|
419
|
= $data->{ $key } && $data->{ $key } !~ /^false$/i |
300
|
|
|
|
|
|
|
? 1 : 0; |
301
|
|
|
|
|
|
|
} |
302
|
|
|
|
|
|
|
elsif ( is_type( $type, 'string' ) && is_format( $format, 'date-time' ) ) { |
303
|
70
|
100
|
|
|
|
238
|
if ( $data->{ $key } eq 'now' ) { |
304
|
57
|
|
|
|
|
361
|
$replace{ $key } = Time::Piece->new->datetime; |
305
|
|
|
|
|
|
|
} |
306
|
|
|
|
|
|
|
} |
307
|
|
|
|
|
|
|
} |
308
|
172
|
|
|
|
|
2930
|
+{ %$data, %replace }; |
309
|
|
|
|
|
|
|
} |
310
|
|
|
|
|
|
|
|
311
|
|
|
|
|
|
|
# Some databases can know other formats |
312
|
|
|
|
|
|
|
my %db_formats = map { $_ => 1 } qw( date time date-time binary ); |
313
|
|
|
|
|
|
|
|
314
|
|
|
|
|
|
|
sub read_schema { |
315
|
106
|
|
|
106
|
0
|
70927
|
my ( $self, @table_names ) = @_; |
316
|
106
|
50
|
|
|
|
442
|
my $schema = %Local::Test::SCHEMA ? \%Local::Test::SCHEMA : $self->schema; |
317
|
106
|
|
|
|
|
32482
|
my $cloned = dclone $schema; |
318
|
106
|
|
|
|
|
1151
|
delete @$cloned{@Local::Test::SCHEMA_ADDED_COLLS}; # ones not in the "database" at all |
319
|
|
|
|
|
|
|
# zap all things that DB can't know about |
320
|
106
|
|
|
|
|
536
|
for my $c ( values %$cloned ) { |
321
|
545
|
|
|
|
|
957
|
delete $c->{'x-list-columns'}; |
322
|
545
|
|
|
|
|
755
|
for my $p ( values %{ $c->{properties} } ) { |
|
545
|
|
|
|
|
1439
|
|
323
|
3150
|
|
|
|
|
4943
|
delete @$p{ qw(description pattern title) }; |
324
|
3150
|
100
|
100
|
|
|
7424
|
if ( $p->{format} && !$db_formats{ $p->{format} } ) { |
325
|
413
|
|
|
|
|
774
|
delete $p->{format}; |
326
|
|
|
|
|
|
|
} |
327
|
|
|
|
|
|
|
} |
328
|
|
|
|
|
|
|
} |
329
|
106
|
100
|
|
|
|
714
|
return @table_names ? @$cloned{ @table_names } : $cloned; |
330
|
|
|
|
|
|
|
} |
331
|
|
|
|
|
|
|
|
332
|
1
|
|
|
1
|
0
|
6288
|
sub supports { grep { $_[1] eq $_ } 'complex-type' } |
|
1
|
|
|
|
|
6
|
|
333
|
|
|
|
|
|
|
|
334
|
|
|
|
|
|
|
1; |
335
|
|
|
|
|
|
|
|
336
|
|
|
|
|
|
|
__END__ |