Fixed typo
[dbsrgits/DBM-Deep.git] / lib / DBM / Deep / SQL / Array.pm
CommitLineData
4f0f6fff 1package DBM::Deep::SQL::Array;
2
3use strict;
4use warnings FATAL => 'all';
5
6BEGIN {
7 use base 'DBM::Deep::SQL::Util';
8
9 use Storable 'nfreeze', 'thaw';
10}
11
12sub _get_self
13{
14 eval { local $SIG{'__DIE__'}; tied( @{$_[0]} ) } || $_[0];
15}
16
17sub _size
18{
19 my ($obj) = @_;
20 my $sq = $obj->_select(
21 'table' => 'rec_array_item',
22 'fields' => 'max(pos)',
23 'where' => {
24 'array' => $obj->{'id'},
25 },
26 );
27 if (defined $sq->[0]->[0])
28 {
29 return $sq->[0]->[0] + 1;
30 }
31 return 0;
32}
33
34sub _clear
35{
36 my ($obj) = @_;
37 my $sz = $obj->_size();
38 foreach my $i (1..$sz)
39 {
40 $obj->_delete($i - 1);
41 }
42 $obj->{'cache'} = [];
43}
44
45sub _delete
46{
47 my ($obj, $i) = @_;
48 my $q = $obj->_select(
49 'table' => 'rec_array_item',
50 'fields' => ['value_type', 'value_data', 'id'],
51 'where' => {
52 'array' => $obj->{'id'},
53 'pos' => $i,
54 },
55 );
56 if (scalar @$q)
57 {
58 my $dt = $q->[0]->[0];
59 if ($dt eq 'text' || $dt eq 'data')
60 {
61 $obj->_delete_sql('rec_value_'. $dt, {'id' => $q->[0]->[1]});
62 }
63 elsif ($dt eq 'hash')
64 {
65 my $rec = $obj->_tiehash($q->[0]->[1]);
66 %$rec = ();
67 $obj->_delete_sql('rec_hash', {'id' => $q->[0]->[1]});
68 $obj->_delete_sql('rec_item', {'id' => $q->[0]->[1]});
69 }
70 elsif ($dt eq 'array')
71 {
72 my $rec = $obj->_tiearray($q->[0]->[1]);
73 @$rec = ();
74 $obj->_delete_sql('rec_array', {'id' => $q->[0]->[1]});
75 $obj->_delete_sql('rec_item', {'id' => $q->[0]->[1]});
76 }
77 $obj->_delete_sql('rec_array_item', {'id' => $q->[0]->[2]});
78 }
79 delete $obj->{'cache'}->[$i];
80}
81
82sub _set_cache
83{
84 my ($obj, $pos, $val) = @_;
85 $obj->{'cache'}->[$pos] = $val;
86}
87
88sub _get_cache
89{
90 my ($obj, $pos, $vref) = @_;
91 if (exists $obj->{'cache'}->[$pos])
92 {
93 $$vref = $obj->{'cache'}->[$pos];
94 return 1;
95 }
96 return undef;
97}
98
99sub _exists
100{
101 my ($obj, $i) = @_;
102 if (exists $obj->{'cache'}->[$i])
103 {
104 return 1;
105 }
106 my $c = $obj->_select(
107 'table' => 'rec_array_item',
108 'fields' => 'count(id)',
109 'where' => {
110 'array' => $obj->{'id'},
111 'pos' => $i,
112 },
113 )->[0]->[0];
114 return $c;
115}
116
117sub _data
118{
119 my ($obj, $i) = @_;
120 my $q = $obj->_select(
121 'table' => 'rec_array_item',
122 'fields' => ['value_type', 'value_data'],
123 'where' => {
124 'array' => $obj->{'id'},
125 'pos' => $i,
126 },
127 );
128 if (scalar @$q)
129 {
130 my $dt = $q->[0]->[0];
131 my $val = $q->[0]->[1];
132 if ($dt eq 'value')
133 {
134 return $val;
135 }
136 elsif ($dt eq 'text')
137 {
138 my $dq = $obj->_select(
139 'table' => 'rec_value_text',
140 'fields' => 'data',
141 'where' => {
142 'id' => $val,
143 },
144 );
145 return $dq->[0]->[0];
146 }
147 elsif ($dt eq 'data')
148 {
149 my $dq = $obj->_select(
150 'table' => 'rec_value_data',
151 'fields' => 'data',
152 'where' => {
153 'id' => $val,
154 },
155 );
156 if (scalar @$dq)
157 {
158 my $rec = thaw($dq->[0]->[0]);
159 return $rec;
160 }
161 return undef;
162 }
163 elsif ($dt eq 'array')
164 {
165 my $rec = $obj->_tiearray($val);
166 if ($obj->{'prefetch'})
167 {
168 (tied(@$rec))->_prefetch();
169 }
170 return $rec;
171 }
172 elsif ($dt eq 'hash')
173 {
174 my $rec = $obj->_tiehash($val);
175 if ($obj->{'prefetch'})
176 {
177 (tied(%$rec))->_prefetch();
178 }
179 return $rec;
180 }
181 }
182 return undef;
183}
184
185sub _tiearray
186{
187 my ($obj, $id) = @_;
188 my $rec = undef;
189 tie(@$rec, 'DBM::Deep::SQL::Array', (
190 'dbi' => $obj->{'dbi'},
191 'id' => $id,
192 'prefetch' => $obj->{'prefetch'},
193 ));
194 bless $rec, 'DBM::Deep::SQL::Array';
195 return $rec;
196}
197
198sub _tiehash
199{
200 my ($obj, $id) = @_;
201 my $rec = undef;
202 tie(%$rec, 'DBM::Deep::SQL::Hash', (
203 'dbi' => $obj->{'dbi'},
204 'id' => $id,
205 'prefetch' => $obj->{'prefetch'},
206 ));
207 bless $rec, 'DBM::Deep::SQL::Hash';
208 return $rec;
209}
210
211sub _prefetch
212{
213 my ($obj) = @_;
214 my $pd = $obj->_select(
215 'table' => 'rec_array_item',
216 'fields' => ['pos', 'value_type', 'value_data'],
217 'where' => {
218 'array' => $obj->{'id'},
219 },
220 );
221 my @data = ();
222 my @datapos = ();
223 my @text = ();
224 my @textpos = ();
225 my @array = ();
226 foreach my $r (@$pd)
227 {
228 my $i = $r->[0];
229 my $vt = $r->[1];
230 my $val = $r->[2];
231 if ($vt eq 'value')
232 {
233 $array[$i] = $val;
234 }
235 elsif ($vt eq 'text')
236 {
237 push @textpos, $i;
238 push @text, $val;
239 }
240 elsif ($vt eq 'data')
241 {
242 push @datapos, $i;
243 push @data, $val;
244 }
245 elsif ($vt eq 'array')
246 {
247 my $rec = $obj->_tiearray($val);
248 if ($obj->{'prefetch'})
249 {
250 (tied(@$rec))->_prefetch();
251 }
252 $array[$i] = $rec;
253 }
254 elsif ($vt eq 'hash')
255 {
256 my $rec = $obj->_tiehash($val);
257 if ($obj->{'prefetch'})
258 {
259 (tied(@$rec))->_prefetch();
260 }
261 $array[$i] = $rec;
262 }
263 }
264 if (scalar @text)
265 {
266 my $ids = join(',', @text);
267 my $tq = $obj->_select(
268 'table' => 'rec_value_text',
269 'fields' => ['id', 'data'],
270 'where' => "id in ($ids)",
271 );
272 my %data = map {$_->[0] => $_->[1]} @$tq;
273 foreach my $x (0..$#text)
274 {
275 my $id = $text[$x];
276 my $i = $textpos[$x];
277 $array[$i] = $data{$id};
278 }
279 }
280 if (scalar @data)
281 {
282 my $ids = join(',', @data);
283 my $tq = $obj->_select(
284 'table' => 'rec_value_data',
285 'fields' => ['id', 'data'],
286 'where' => "id in ($ids)",
287 );
288 my %d = map {$_->[0] => $_->[1]} @$tq;
289 foreach my $x (0..$#data)
290 {
291 my $id = $data[$x];
292 my $i = $datapos[$x];
293 if (defined $d{$id})
294 {
295 $array[$i] = thaw($d{$id});
296 }
297 }
298 }
299 return $obj->{'cache'} = \@array;
300}
301
302sub TIEARRAY
303{
304 my $class = shift;
305 my %prm = @_;
306 my $obj = \%prm;
307 $obj->{'cache'} = [];
308 bless $obj, $class;
309 return $obj;
310}
311
312sub FETCH
313{
314 my ($tobj, $i) = @_;
315 my $obj = $tobj->_get_self();
316 my $val = undef;
317 if ($obj->_get_cache($i, \$val))
318 {
319 return $val;
320 }
321 $val = $obj->_data($i);
322 if (defined $val)
323 {
324 $obj->_set_cache($i, $val);
325 }
326 return $val;
327}
328
329sub STORE
330{
331 my ($tobj, $i, $val) = @_;
332 my $obj = $tobj->_get_self();
333 my $dval = $val;
334 my $vt;
335 $val = '' unless (defined $val);
336 if (ref $val)
337 {
338 my $done = 0;
339 unless ($obj->{'serialize'})
340 {
341 if ($val =~ /HASH/)
342 {
343 my $id = $obj->_create('hash');
344 my $ta = $obj->_tiehash($id);
345 $dval = $ta;
346 foreach my $k (keys %$val)
347 {
348 $ta->{$k} = $val->{$k};
349 }
350 $vt = 'hash';
351 $val = $id;
352 $done = 1;
353 }
354 elsif ($val =~ /ARRAY/)
355 {
356 my $id = $obj->_create('array');
357 my $ta = $obj->_tiearray($id);
358 $dval = $ta;
359 foreach my $i (0..$#{$val})
360 {
361 $ta->[$i] = $val->[$i];
362 }
363 $vt = 'array';
364 $val = $id;
365 $done = 1;
366 }
367 }
368 unless ($done)
369 {
370 my $data = nfreeze($val);
371 $val = $obj->_create('value_data', {
372 'data' => $data,
373 });
374 $vt = 'data';
375 }
376 }
377 elsif (length($val) > 255)
378 {
379 $val = $obj->_create('value_text', {
380 'data' => $val,
381 });
382 $vt = 'text';
383 }
384 else
385 {
386 $vt = 'value';
387 }
388 my $c = $obj->_select(
389 'table' => 'rec_array_item',
390 'fields' => ['value_type', 'id'],
391 'where' => {
392 'array' => $obj->{'id'},
393 'pos' => $i,
394 },
395 );
396 my $create = 1;
397 if (scalar @$c)
398 {
399 if ($c->[0]->[0] eq 'value')
400 {
401 $create = 0;
402 $obj->_update(
403 'table' => 'rec_array_item',
404 'fields' => {
405 'value_type' => $vt,
406 'value_data' => $val,
407 },
408 'where' => {
409 'id' => $c->[0]->[1],
410 },
411 );
412 }
413 else
414 {
415 $obj->_delete($i);
416 }
417 }
418 if ($create)
419 {
420 $obj->_create('array_item', {
421 'array' => $obj->{'id'},
422 'pos' => $i,
423 'value_data' => $val,
424 'value_type' => $vt,
425 });
426 }
427 $obj->_set_cache($i, $dval);
428 return $dval;
429}
430
431sub FETCHSIZE
432{
433 my ($tobj) = @_;
434 my $obj = $tobj->_get_self();
435 return $obj->_size();
436}
437
438sub EXISTS
439{
440 my ($tobj, $i) = @_;
441 my $obj = $tobj->_get_self();
442 return $obj->_exists($i);
443}
444
445sub DELETE
446{
447 my ($tobj, $i) = @_;
448 my $obj = $tobj->_get_self();
449 return $obj->_delete($i);
450}
451
452sub CLEAR
453{
454 my ($tobj) = @_;
455 my $obj = $tobj->_get_self();
456 return $obj->_clear();
457}
458
459sub PUSH
460{
461 my ($tobj, @list) = @_;
462 my $obj = $tobj->_get_self();
463 my $last = $obj->_size();
464 foreach my $i (0..$#list)
465 {
466 $tobj->STORE($last + $i, $list[$i]);
467 }
468 return $obj->_size();
469}
470
471sub POP
472{
473 my ($tobj) = @_;
474 my $obj = $tobj->_get_self();
475 my $top = $obj->_size();
476 unless ($top > 0)
477 {
478 return undef;
479 }
480 my $val = $obj->_data($top - 1);
481 $obj->_delete($top - 1);
482 return $val;
483}
484
485sub SHIFT
486{
487 my ($tobj) = @_;
488 my $obj = $tobj->_get_self();
489 my $top = $obj->_size();
490 unless ($top > 0)
491 {
492 return undef;
493 }
494 my $val = $obj->_data(0);
495 $obj->_delete(0);
496 my $sql = 'update rec_array_item set pos=pos-1 where array=? order by pos asc';
497 $obj->{'dbi'}->query($sql, $obj->{'id'});
498 return $val;
499}
500
501sub UNSHIFT
502{
503 my ($tobj, $val) = @_;
504 my $obj = $tobj->_get_self();
505 my $top = $obj->_size();
506 if ($top > 0)
507 {
508 my $sql = 'update rec_array_item set pos=pos+1 where array=? order by pos desc';
509 $obj->{'dbi'}->query($sql, $obj->{'id'});
510 }
511 return $tobj->STORE(0, $val);
512}
513
514sub EXTEND
515{
516 # Not needed
517 return;
518}
519
520sub SPLICE
521{
522 my ($tobj, $offset, $len, @list) = @_;
523 my $obj = $tobj->_get_self();
524 my $cache = $obj->{'cache'};
525 $obj->{'cache'} = [];
526 unless (defined $offset)
527 {
528 $offset = 0;
529 }
530 if (length($offset) < 0)
531 {
532 die('Splice with negative offset not supported'); # TODO
533 }
534 unless (defined $len)
535 {
536 $len = $obj->_size() - $offset;
537 }
538 if (length($len) < 0)
539 {
540 die('Splice with negative length not supported'); # TODO
541 }
542 if ($offset < $#{$cache} || $offset == 0)
543 {
544 splice(@$cache, $offset, $len, @list);
545 }
546 else
547 {
548 $cache = [];
549 }
550 my $lc = (wantarray) ? 1 : 0;
551 my @res = ();
552 if ($len > 0)
553 {
554 foreach my $i (0..($len - 1))
555 {
556 my $k = $offset + $i;
557 if ($lc || $i == ($len - 1))
558 {
559 my $rc = $tobj->FETCH($k);
560 my $cl = $obj->_clone_tree($rc);
561 push @res, $cl;
562 }
563 $obj->_delete($k);
564 }
565 }
566 my $elems = scalar @list;
567 my $diff = $elems - $len;
568 if ($elems > 0 || $diff < 0)
569 {
570 my $st = $offset + $len - 1;
571 my $dir = ($diff > 0) ? 'desc' : 'asc';
572 my $sql = 'update rec_array_item set pos=pos+? where array=? and pos > ? order by pos '. $dir;
573 $obj->{'dbi'}->query($sql, $diff, $obj->{'id'}, $st);
574 foreach my $i (0..$#list)
575 {
576 $tobj->STORE($offset + $i, $list[$i]);
577 }
578 }
579 $obj->{'cache'} = $cache;
580 if ($lc)
581 {
582 return @res;
583 }
584 else
585 {
586 return $res[0];
587 }
588}
589
590sub id
591{
592 my ($tobj) = @_;
593 my $obj = $tobj->_get_self();
594 return $obj->{'id'};
595}
596
5971;
598