File Coverage

blib/lib/TableMap.pm
Criterion Covered Total %
statement 4 6 66.6
branch n/a
condition n/a
subroutine 2 2 100.0
pod n/a
total 6 8 75.0


line stmt bran cond sub pod time code
1             =head1 NAME
2              
3             TableMap - Maps relational tables into hashes
4              
5             =head1 SYNOPSIS
6              
7             use DBI;
8             $dbh=DBI->connect(...);
9              
10             use TableMap;
11              
12             $database=new TableMap::DB(
13             dbh => $dbh,
14             # DBI Database handler
15             seq_mode => "ora",
16             # Sequence handling mode
17             # "ora": "select seqence.currval from dual";
18             # "pg" : "select sequence.last_value";
19             # db systems, which doesn't support sequences currently
20             # doesn't supported (insert won't work)
21             prepare_cached => 0,
22             # Can use "prepare_cached" method of the DBI?
23             # This causes problems for me, and that's why the
24             # default is now 0. This param is not mandatory.
25             );
26              
27             # You can use connect hash to specify connect parameters directly.
28             # In this case you doesn't need to specify "dbh" parameter:
29             # $database=new TableMap::DB(
30             # connect=> [$data_source, $username, $auth, \%attr],
31             # seq_currval => ...
32             # );
33            
34             $company=$database->new_table (
35             table => "company", # Table name, mandatory
36             key => "id", # Primary Key for the table
37             seq => "seq_company",# Sequence name for key field generation.
38             # Mandatory only if "insert" is in use
39             );
40              
41             # $database->new_table(...)
42             # is the same as
43             # new TableMap ( db => $database, ... )
44              
45             $user =$database->new_table (
46             table => "users",
47             key => "id",
48             seq => "seq_users",
49             ref => { company_id => [ $company, "user" ] },
50             # This can be used for connecting tables.
51             # This is similar to the SQL phrase:
52             #
53             # .. company_id int references company (id),
54             #
55             # only the key field can be referenced.
56             );
57              
58             %company_14_users= % {$company->{14}->user };
59              
60             # All user IDs
61             keys %$user;
62              
63             # Sets Company #14 Data:
64             $company_14 = $company->{14};
65             $company_14->{tax_num} = "123456";
66             $company_14->{phone1} = "+42456245546";
67             $company_14->write;
68              
69             # Wrong example:
70             # $company->{14}->{tax_num} = "123456"
71             # $company->{14}->write;
72             # This doesn't work, because it always create a new Row object,
73             # and the cache is stored per object.
74              
75             # Select a sub-relation
76             $table=$user->select("company_id = ?",$id);
77              
78             # Select with constraint
79             $user->constraint( company_id => $id );
80              
81             # New record
82             $id=$company->new(
83             { name=>"abc",
84             phone1=>"30/123567",
85             mobile=>"20/1234" } );
86             if ($id) { print "Id: $id\n"; } else { print "Insert failed: "; };
87              
88             # Delete record
89             delete $company->{13};
90             %{ $company->{13} }=();
91              
92             =head1 DESCRIPTION
93              
94             This class is designed for mapping a table into a perl hash, which has keys (these are the primary keys of the table), and the values are the rows, represented by a hash.
95              
96             =head2 Basic Usage
97              
98             You can create TableMap objects for tables. You must specify a parameter hash to the constructor, which has the following keys:
99              
100             =over 4
101              
102             =item db
103              
104             This is a reference to a TableMap::DB module. Normally you create a new TableMap object by the method of the "new_table" of a TableMap::DB instance, then you may not specify this.
105              
106             =item table
107              
108             Specifies the table name
109              
110             =item key
111              
112             Specifies the primary key. This must be specified, so if you don't have primary keys in your table, you cannot use the TableMap (for the whole table. You can use it for a subset of rows specified by the "constraint" param).
113              
114             =item seq
115              
116             If you want to use "insert" with self-incremental keys, you must specify this. Database servers, which doesn't implement sequences (mySQL) currently not supported.
117              
118             =item ref
119              
120             Creating a 1:N reference. The value is a hash reference, where the keys are database fields, and the values contains the reference information in an array reference:
121              
122             ref => {
123             field1 => [ $table1, "function1" ],
124             field2 => [ $table2, "function2" ],
125             };
126              
127             In the example above you can use the reference field (company_id) from the "user" table to query the company in which the user work: $company_name = $user->{17}->company_id->{name}.
128              
129             function1 is the name of the function, which can be used for the back-reference, eg. can be used to determine the user-ids in one company: @user_ids= keys %{ $company->{45}->users }. "users" is the function name in this example.
130              
131             =item where
132              
133             Specifies a "where" condition for selecting id-s, so you can select a subset of keys with this. Also available with the "search" function:
134              
135             @user_ids= keys %{ $table->search("age > 25") };
136              
137             or
138              
139             $table=new TableMap (table => "table", where => "age > 25");
140             @user_ids=keys %$table;
141              
142             =item constraint
143              
144             This is similar to "select", but it can be used only for equality test. The main advantage is that it can be used for deletion and insertion. If you insert something into a table, which has constraint parameter, all the values in the constraint hash is set in the new record. This constraint is used internally, when somebody creates a back reference by a back-reference function.
145              
146             =back 4
147              
148             =head2 References
149              
150             There is two kind of reference in this module. All two are set up by "ref" parameter in the table. If you use a "ref" parameter, then the "back_ref" is automatically created in the other table (if not already exists).
151              
152             =over 4
153              
154             =item ref
155              
156             $user->company_id gives a TableMap::Row record, which is a ROW in the company table. Each hash keys are field names.
157              
158             =item back_ref
159              
160             $company->users gives a TableMap object, which is a COLLECTION of rows (represented by a hash), which gives back the employees of the companies. (you can use "keys ..." expression for the ids).
161              
162             =back 4
163              
164             =head2 Caching
165              
166             All the sql queries are cached in this module. This must be rethink, because sometimes it is not the best solution.
167             I want some extra parameter for caching in the newer versions. Now all the query results are cached for 10 seconds.
168              
169             The Cache object is $TableMap::cache, and it can be invalidated by the $TableMap::cache->invalidate_cache call.
170              
171             The cache is hierarchical (it is stored in tree structure).
172              
173             For more information on the cache you can see the source code.
174              
175             =head2 Performance
176              
177             This module is NOT the most efficient method for getting data from the database. It is written to avoid endless sql-query-typing with minimal performance loss.
178              
179             The module uses two kind of sql queries:
180              
181             =over 4
182              
183             =item select key from table
184              
185             This is used for querying all the keys from a table. This can be affected by the "constraint" and the "where" parameter.
186              
187             =item select * from table where id=1234
188              
189             This is used for querying all the fields of one row. This can be affected by the "constraint" parameter, but not thw "where".
190              
191             =back 4
192              
193             Sometimes querying the whole table is more effective, (when you have enough memory), but currently it is only a planned parameter.
194              
195             =head2 TODO
196              
197             =over 2
198              
199             =item *
200              
201             This module is now usable for one purpose. I have profiled it, and I've found that the "read_data" function is the most time-consuming. This must be handled by re-organizing the cache.
202              
203             =item *
204              
205             I want to add a parameter for setting for the caching mode. Sometimes no caching is allowed, but sometimes te full table must be cached for good performance.
206              
207             =item *
208              
209             Cahce reorganization is needed, because reaching a cached data is quite slow.
210              
211             =back 2
212              
213             =head1 COPYRIGHT
214              
215             Copyrigh (c) 2000 Balázs Szabó (dLux)
216             All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
217              
218             =head1 AUTHOR
219              
220             dLux
221              
222             =cut
223              
224             package TableMap::DB;
225 1     1   7544 use strict;
  1         2  
  1         49  
226             require Storable;
227 1     1   1692 use DBI;
  0            
  0            
228             use Carp qw(confess cluck);
229             use vars qw($DEBUG);
230              
231             sub new { my ($o,%param)=@_;
232             my $s=\%param;
233             bless ($s,$o);
234             if (exists $s->{connect}) {
235             $s->{dbh}=DBI->connect(@{ $s->{connect} });
236             };
237             confess "Not enough parameter" if !$s->{dbh};
238             $s->set_seq_mode($s->{seq_mode});
239             return $s;
240             };
241              
242             sub sql { my ($s,$sql,@array)=@_;
243             my $sth;
244             eval {
245             if (ref($sql) eq 'ARRAY') {
246             $sth=$s->{prepare_cached} ?
247             $s->{dbh}->prepare_cached($sql->[0]) :
248             $s->{dbh}->prepare($sql->[0]);
249             } else {
250             $sth=$s->{dbh}->prepare($sql);
251             };
252             cluck $s->{dbh}->errstr if $s->{dbh}->err or $DEBUG;
253             $sth->execute(@array);
254             };
255             return $sth if $s->{quiet};
256             if ($s->{dbh}->err || $@ || $DEBUG) {
257             $sql="@$sql" if ref($sql) eq 'ARRAY';
258             cluck "$@ ".$s->{dbh}->errstr."\nQUERY: \"$sql\"".
259             (@array ? " Parameters: ".join(",",@array) : "")."\n";
260             };
261             return $sth;
262             };
263              
264             sub set_seq_mode { my ($s,$mode)=@_;
265             $s->{seq_query}=
266             $mode eq 'ora' ? "select %s.currval from dual;" :
267             $mode eq 'pg' ? "select %s.last_value" :
268             undef;
269             };
270              
271             sub select_currval { my ($s,$sequence)=@_;
272             cluck "No sequence handling installed!" if !$s->{seq_query};
273             return $s->sql([sprintf($s->{seq_query},$sequence)]);
274             };
275              
276             sub new_table { my ($s,@params)=@_;
277             return new TableMap( db=> $s, @params );
278             };
279              
280             package TableMap;
281             use strict;
282             use Carp qw(confess cluck);
283             use vars qw($DEBUG $VERSION $cache);
284              
285             $VERSION='1.0';
286              
287             sub cache { $TableMap::cache; };
288              
289             sub new { my ($o,%param)=@_;
290             my $s={};
291             bless($s,$o);
292             confess "Not enough Parameter"
293             if !exists $param{table} || !exists $param{key} || !exists $param{db};
294             tie (%$s,"TableMap::TIE",$s,%param);
295             (tied %$s)->make_back_refs;
296             return $s;
297             };
298              
299             sub write { my ($ss)=@_; my $s=tied %$ss;
300             my @r=values %{ $s->{dirty_rows} };
301             foreach my $r (@r) { $r->write; };
302             };
303              
304             sub write_cascade { my ($ss)=@_; my $s=tied %$ss;
305             $ss->write;
306             foreach my $v (values %{ $s->{param}->{back_ref} }) {
307             $v->[0]->write_cascade;
308             };
309             };
310              
311             sub select { my ($ss,$sql,@par)=@_; my $s=tied %$ss;
312             my $p=$s->clone_param();
313             $p->{"where"}= exists $p->{"where"} ?
314             "(".$p->{"where"}.") and ($sql)" : $sql;
315             push @{ $p->{"query_param"} },@par;
316             return new TableMap(%$p);
317             };
318              
319             sub constraint { my ($ss,%cons)=@_; my $s=tied %$ss;
320             my $p=$s->clone_param();
321             foreach my $i (keys %cons) {
322             $p->{"constraint"}->{$i}=$cons{$i};
323             };
324             return new TableMap(%$p);
325             };
326              
327             sub insert { my ($ss,$data)=@_; my $s=tied %$ss;
328             my $key_field=$s->{param}->{key};
329             confess "Specify \"seq\" if you want to insert"
330             if !exists $s->{param}->{seq} && !exists $data->{$key_field};
331             $ss->insert_row($data);
332             };
333              
334             sub insert_row { my ($ss,$data)=@_; my $s=tied %$ss;
335             my $key_field=$s->{param}->{key};
336             my $constraint=$s->{param}->{"constraint"};
337             my $db=$s->{param}->{db};
338             if ($constraint) {
339             foreach my $k (keys %$constraint) {
340             $data->{$k}=$constraint->{$k};
341             };
342             };
343             my (@sql1,@sql2,@data);
344             foreach my $k (keys %$data) {
345             push @sql1,$k;
346             push @sql2,"?";
347             push @data,$data->{$k};
348             };
349             $db->sql(["insert into ".$s->{param}->{table}." (".
350             join(",",@sql1).") values (".join(",",@sql2).")"],@data);
351             cache->invalidate_cache([$s->{param}->{table}]);
352             return undef if $db->{dbh}->err;
353             if (!exists $data->{$key_field}) {
354             my $sth=$db->select_currval($s->{param}->{seq});
355             my ($seq)=$sth->fetchrow;
356             $sth->finish;
357             return $seq;
358             } else {
359             return $data->{$key_field};
360             };
361             };
362              
363             sub delete { my ($ss,$key)=@_; my $s=tied %$ss;
364             my $constraint=$s->{param}->{"constraint"};
365             my $db=$s->{param}->{db};
366             my $cons; my @cons;
367             if ($constraint) {
368             foreach my $k (keys %$constraint) {
369             $cons.=" and $k=?";
370             push @cons,$constraint->{$k};
371             };
372             };
373             $db->sql(["delete from ".$s->{param}->{table}." where ".
374             $s->{param}->{key}."=?".$cons],$key,@cons);
375             cache->invalidate_cache([$s->{param}->{table}]);
376             foreach my $v (values %{ $s->{param}->{back_ref} }) {
377             # Invalidate every back-referenced table
378             cache->invalidate_cache([ $v->[0]->{param}->{table} ]);
379             };
380             };
381              
382             #####################################
383             package TableMap::TIE;
384             use strict;
385             use Carp qw(confess cluck);
386             use vars qw($DEBUG);
387              
388             sub cache { $TableMap::cache; };
389             sub TIEHASH { my ($o,$main_obj,%param)=@_;
390             my $s={
391             param =>\%param,
392             main_obj =>$main_obj,
393             dirty_row=>{},
394             };
395             $s->{param}->{back_ref} ||= {};
396             bless ($s,$o);
397             return $s;
398             };
399              
400             sub make_back_refs { my ($s)=@_;
401             my $main_obj=$s->{main_obj};
402             if (exists $s->{param}->{"ref"}) {
403             foreach my $k (keys %{ $s->{param}->{"ref"} }) {
404             my ($ref_table,$function)=@{ $s->{param}->{"ref"}->{$k} };
405             (tied %$ref_table)->{param}->{back_ref}->{$function}||=[$main_obj,$k];
406             # write only when no data is already written to it
407             };
408             };
409             };
410              
411             sub FIRSTKEY { my ($s)=@_;
412             my (@where,@qp,@path);
413             push @path,$s->{param}->{table},$s->{param}->{key},"__all_keys__";
414             my $constraint=$s->{param}->{constraint};
415             if ($constraint) {
416             foreach my $k (sort keys %$constraint) {
417             push @where,"$k=?";
418             push @path,$k,$constraint->{$k};
419             push @qp,$constraint->{$k};
420             };
421             };
422             if (exists $s->{param}->{"where"}) {
423             push @where, $s->{param}->{"where"};
424             push @path,"where ".$s->{param}->{where},@{ $s->{param}->{"query_param"} };
425             push @qp, @{ $s->{param}->{"query_param"} };
426             };
427             my $array=cache->get_array($s->{param}->{db},[@path],
428             "select ".$s->{param}->{key}." from ".$s->{param}->{table}.
429             (@where ? " where ".join(" and ",@where) : ""), [@qp]
430             );
431             $s->{keys}=$array;
432             return $s->{keys}->[0]->[0];
433             };
434              
435             sub NEXTKEY { my ($s,$lastkey)=@_;
436             my $key= ++$s->{keycount};
437             return undef if !exists $s->{"keys"};
438             if ($key >= @{ $s->{"keys"} }) {
439             delete $s->{keycount};
440             delete $s->{"keys"};
441             return undef;
442             };
443             return $s->{"keys"}->[$key]->[0];
444             };
445              
446             sub STORE { my ($s,$key,$val)=@_;
447             # unimplemented
448             cluck "STORE unimplemented";
449             };
450              
451             # delete $hash->{key};
452             sub DELETE { my ($s,$key)=@_;
453             my $main_obj=$s->{main_obj};
454             $main_obj->delete($key);
455             };
456              
457             # CLEAR: %{ $hash->{key} }=();
458             sub CLEAR { &DELETE; };
459              
460             sub FETCH { my ($s,$key)=@_;
461             return new TableMap::Row( $s, $key);
462             };
463              
464             sub EXISTS { &FETCH; };
465              
466             sub clone_param { my ($s)=@_;
467             my $p=$s->{param};
468             my $r; %$r=%$p;
469             foreach my $k (qw(constraint query_param)) {
470             $r->{$k}=Storable::dclone($p->{$k}) if exists $p->{$k};
471             };
472             return $r;
473             };
474              
475             #####################################
476             package TableMap::Row;
477             use strict;
478             use Carp qw(confess cluck);
479             use vars qw($DEBUG $AUTOLOAD);
480              
481             sub cache { $TableMap::cache; };
482              
483             sub new { my ($o,$table,$key)=@_;
484             my $s={};
485             bless $s,$o;
486             return tie(%$s,"TableMap::Row::TIE",$table,$key) ? $s : undef;
487             };
488              
489             sub write { my ($ss)=@_; my $s=tied %$ss;
490             my $param=$s->{table}->{param};
491             my $db=$param->{db};
492             my $key_field=$param->{key};
493             my $key_value=$s->{data}->{$key_field};
494             my $table=$param->{table};
495             my $sql="update $table set ";
496             my @sql; my @data;
497             foreach my $k (keys %{ $s->{newdata} }) {
498             push @sql, "$k=?";
499             push @data,$s->{newdata}->{$k};
500             };
501             my $constraint=$param->{"constraint"};
502             my @where="$key_field=?";
503             push @data,$key_value;
504             if ($constraint) {
505             foreach my $k (keys %$constraint) {
506             push @where,"$k=?";
507             push @data,$constraint->{$k};
508             };
509             };
510             if (@sql) {
511             $db->sql(["update $table set ".join(",",@sql)." where ".
512             join(" and ",@where)],@data);
513             cache->invalidate_cache([ $table ]);
514             };
515             $s->{newdata}={};
516             delete $s->{table}->{dirty_rows}->{$key_value};
517             };
518              
519             sub AUTOLOAD { my ($ss)=@_; my $s=tied %$ss;
520             my ($sub) = $AUTOLOAD =~ /.*::(.*)/o;
521             my $param=$s->{table}->{param};
522             my $back_ref=$param->{back_ref};
523             my $ref=$param->{'ref'};
524             if (exists $back_ref->{$sub}) {
525             my $param=(tied %{ $back_ref->{$sub}->[0] })->clone_param();
526             $param->{"constraint"}->{ $back_ref->{$sub}->[1] }= $s->{key};
527             return new TableMap (%$param);
528             } elsif (exists $ref->{$sub}) {
529             return undef if !exists $s->{data}->{$sub};
530             return new TableMap::Row( tied %{ $ref->{$sub}->[0] },
531             $s->{data}->{ $sub });
532             };
533             };
534              
535             sub DESTROY {}; # Don't bother AUTOLOAD for it...
536              
537             #####################################
538             package TableMap::Row::TIE;
539             use strict;
540             use Carp qw(confess cluck);
541             use vars qw($DEBUG);
542              
543             sub cache { $TableMap::cache; };
544              
545             sub TIEHASH { my ($o,$table,$key)=@_;
546             my $s={
547             table=>$table,
548             key=>$key,
549             data=>undef,
550             newdata=>undef,
551             };
552             bless $s,$o;
553             $s->read_data;
554             return $s->{data} ? $s : undef;
555             };
556              
557             sub read_data { my ($s)=@_;
558             my ($where,$path,$val);
559             my $param=$s->{table}->{param};
560             if (! ($path=$s->{cache_path})) {
561             my $key=$s->{key};
562             push @$path,$param->{table},$param->{key},$key;
563             my $constraint=$param->{"constraint"};
564             if ($constraint) {
565             foreach my $k (sort keys %$constraint) {
566             push @$path,$k,$constraint->{$k};
567             };
568             };
569             $s->{cache_path}=$path;
570             };
571             $s->{data}=cache->cache_hit($path);
572             if (!$s->{data}) {
573             if (!$s->{query}) {
574             my $key=$s->{key};
575             my $constraint=$param->{"constraint"};
576             push @$where,$param->{key}."=?";
577             push @$val,$key;
578             if ($constraint) {
579             foreach my $k (sort keys %$constraint) {
580             push @$where,"$k=?";
581             push @$val,$constraint->{$k};
582             };
583             };
584             $s->{query}=[$where,$val];
585             } else {
586             ($where,$val)=@{ $s->{query} };
587             };
588             $s->{data}=cache->get_hash_directly($param->{db},
589             $path, "select * from ".$param->{table}." where ".
590             join(" and ",@$where), [@$val]
591             );
592             };
593             };
594              
595             sub FETCH { my ($s,$key)=@_;
596             if (exists $s->{newdata}->{$key}) {
597             return $s->{newdata}->{$key}
598             } else {
599             $s->read_data;
600             cluck "Invalid TableMap Key!" if !exists $s->{data}->{$key};
601             return $s->{data}->{$key};
602             };
603             };
604              
605             sub STORE { my ($s,$key,$value)=@_;
606             my $key_field=$s->{table}->{param}->{key};
607             confess "Cannot modify a key value" if $key eq $key_field;
608             $s->{table}->{dirty_rows}->{ $s->{data}->{$key_field} }=$s;
609             $s->{newdata}->{$key}=$value;
610             };
611              
612             sub DELETE { my ($s,$key)=@_;
613             return undef;
614             };
615              
616             sub CLEAR { my ($s)=@_;
617             return undef;
618             };
619              
620             sub EXISTS { my ($s,$key)=@_;
621             return exists $s->{data}->{$key};
622             };
623              
624             sub FIRSTKEY { my ($s)=@_;
625             my $a= scalar keys %{$s->{data}};
626             each %{$s->{data}};
627             };
628              
629             sub NEXTKEY { my ($s,$lastkey)=@_;
630             each %{$s->{data}};
631             };
632              
633             #####################################
634             package TableMap::Cache;
635             use strict;
636             use Carp qw(confess cluck);
637             use vars qw($DEBUG $CACHE_SECS $CACHE_EXPIRE_PERIOD);
638             $CACHE_SECS=10; # How much time a data is valid in the cache
639             $CACHE_EXPIRE_PERIOD=300;# How often visit through the cache for expired entries
640              
641             sub new { my ($o)=@_;
642             my $s={};
643             bless ($s,$o);
644             return $s;
645             };
646              
647             sub get_array{ my ($s,$db,$cache_path,$sql,$params)=@_;
648             $s->expire_cache;
649             my $a=$s->cache_hit($cache_path);
650             return $a if $a;
651             $a=[];
652             my $sth=$db->sql([$sql],@$params);
653             while (my @row=$sth->fetchrow) {
654             push @$a,[@row];
655             };
656             $sth->finish;
657             $s->cache_write($a,$cache_path);
658             return $a;
659             };
660              
661             sub get_hash { my ($s,$db,$cache_path,$sql,$params)=@_;
662             $s->expire_cache;
663             my $h=$s->cache_hit($cache_path);
664             return $h if $h;
665             &get_hash_directly;
666             };
667              
668             sub get_hash_directly { my ($s,$db,$cache_path,$sql,$params)=@_;
669             $s->expire_cache;
670             my $h;
671             my $sth=$db->sql([$sql],@$params);
672             $h=$sth->fetchrow_hashref;
673             $sth->finish;
674             $s->cache_write($h,$cache_path);
675             return $h;
676             };
677              
678             sub cache_hit{ my ($s,$path)=@_;
679             return undef if !exists $s->{cache};
680             my $walk=$s->{cache};
681             for (my $i=0; $i<@$path; $i++) {
682             my $key=$path->[$i];
683             return undef if !exists $walk->[0]->{$key};
684             $walk=$walk->[0]->{$key};
685             };
686             return undef if $walk->[1]
687             return $walk->[2];
688             };
689              
690             sub invalidate_cache { my ($s,$path)=@_;
691             if (! @$path ) { $s->{cache}=[{},0,undef]; return; };
692             my $walk=$s->{cache};
693             for (my $i=0; $i<@$path-1; $i++) {
694             my $k=$path->[$i];
695             return if !exists $walk->[0]->{$k};
696             $walk=$walk->[0]->{$k};
697             };
698             $walk->[0]->{ $path->[-1] }=[{},0,undef];
699             };
700              
701             sub expire_cache { my ($s)=@_;
702             return if $s->{expire_time}>time;
703             $s->expire_cache_what($s->{cache});
704             $s->{expire_time}=time+$CACHE_EXPIRE_PERIOD;
705             };
706              
707             sub expire_cache_what { my ($s,$what)=@_;
708             my $keep_me=1;
709             if ($what->[1]
710             $keep_me=0;
711             $what->[2]=undef;
712             };
713             my @k=keys %{ $what->[0] };
714             foreach my $k (@k) {
715             my $keep_it=$s->expire_cache_what($what->[0]->{$k});
716             delete $what->[0]->{$k} if !$keep_it;
717             $keep_me ||= $keep_it;
718             };
719             return $keep_me;
720             };
721              
722             sub cache_write { my ($s,$data,$path)=@_;
723             $s->{cache}=[{},0,undef] if !exists $s->{cache};
724             my $walk=$s->{cache};
725             for (my $i=0; $i<@$path; $i++) {
726             my $k=$path->[$i];
727             $walk->[0]->{$k}=[{},0,undef] if !exists $walk->[0]->{$k};
728             $walk=$walk->[0]->{$k};
729             };
730             $walk->[1]=time;
731             $walk->[2]=$data;
732             };
733              
734             $TableMap::cache=new TableMap::Cache;
735              
736             1;