File Coverage

blib/lib/SQLite_File.pm
Criterion Covered Total %
statement 730 858 85.0
branch 251 402 62.4
condition 74 143 51.7
subroutine 89 99 89.9
pod 18 41 43.9
total 1162 1543 75.3


line stmt bran cond sub pod time code
1             # $Id: SQLite_File.pm 16252 2009-10-09 22:26:34Z maj $
2             #
3             # converted from Bio::DB::SQLite_File for PAUSE upload
4             #
5              
6             =head1 NAME
7              
8             SQLite_File - Tie to SQLite, with DB_File emulation
9              
10             =head1 SYNOPSIS
11              
12             # tie a simple hash to a SQLite DB file
13             my %db;
14             tie(%db, 'SQLite_File', 'my.db');
15              
16             # tie an array
17             my @db;
18             tie(@db, 'SQLite_File', 'my.db');
19              
20             # tie to a tempfile
21             tie(%db, 'SQLite_File', undef);
22              
23             # get attributes of the tied object
24              
25             $SQLite_handle = (tied %db)->dbh;
26             $db_file = (tied %db)->file;
27              
28             # use as an option in AnyDBM_File
29             @AnyDBM_File::ISA = qw( DB_File SQLite_File SDBM );
30             my %db;
31             tie(%db, 'AnyDBM_File', 'my.db', @dbmargs)
32              
33             # Filter with DBM_Filter
34              
35             use DBM_Filter;
36             tie(%db, 'SQLite_File', 'my.db');
37             (tied %db)->Filter_Push('utf8');
38            
39             =head1 DESCRIPTION
40              
41             This module allows a hash or an array to be tied to a SQLite DB via
42             L plus L, in a way that emulates many features of
43             Berkeley-DB-based L. In particular, this module offers another
44             choice for ActiveState users, who may find it difficult to get a
45             working L installed, but can't failover to SDBM due to its
46             record length restrictions. SQLite_File requires
47             L, which has SQLite built-in -- no external application
48             install required.
49              
50             =head2 Key/Value filters
51              
52             The filter hooks C, C, C, and C are honored. L can be used as an API.
53              
54             =head2 DB_File Emulation
55              
56             The intention was to create a DBM that could almost completely substitute for
57             C, so that C could be replaced everywhere in code by
58             C, and things would just work. Currently, it is slightly more
59             complicated than that, but not too much more.
60              
61             Versions of C<$DB_HASH>, C<$DB_BTREE>, and C<$DB_RECNO>, as well as
62             the necessary flags (C, C, C, etc.) are
63             imported by using the L module. The desired
64             constants need to be declared global in the calling program, as well
65             as imported, to avoid compilation errors (at this point). See
66             L below.
67              
68             Arguments to the C function mirror those of C, and all should
69             work the same way. See L.
70              
71             All of C's random and sequential access functions work:
72              
73             get()
74             put()
75             del()
76             seq()
77              
78             as well as the duplicate key handlers
79              
80             get_dup()
81             del_dup()
82             find_dup()
83              
84             C works by finding partial matches, like C.
85             The extra array functions ( C, C, etc. ) are not yet
86             implemented as method calls, though all these functions (including
87             C are available on the tied arrays.
88              
89             Some C fields are functional:
90              
91             $DB_BTREE->{'compare'} = sub { - shift cmp shift };
92              
93             will provide sequential access in reverse lexographic order, for example.
94              
95             $DB_HASH->{'cachesize'} = 20000;
96              
97             will enforce C.
98              
99             =head2 Converting from DB_File
100              
101             To failover to C from C, go from this:
102              
103             use DB_File;
104             # ...
105             $DB_BTREE->{cachesize} = 100000;
106             $DB_BTREE->{flags} = R_DUP;
107             my %db;
108             my $obj = tie( %db, 'DB_File', 'my.db', $flags, 0666, $DB_BTREE);
109              
110             to this:
111            
112             use vars qw( $DB_HASH &R_DUP );
113             BEGIN {
114             @AnyDBM_File::ISA = qw( DB_File SQLite_File )
115             unless @AnyDBM_File::ISA == 1; #
116             }
117             use AnyDBM_File;
118             use AnyDBMImporter qw(:bdb);
119             # ...
120              
121             $DB_BTREE->{cachesize} = 100000;
122             $DB_BTREE->{flags} = R_DUP;
123             my %db;
124             my $obj = tie( %db, 'AnyDBM_File', 'my.db', $flags, 0666, $DB_BTREE);
125              
126             =head1 SEE ALSO
127              
128             L, L, L, L
129              
130             =head1 AUTHOR
131              
132             Mark A. Jensen < MAJENSEN -at- cpan -dot- org >
133             http://fortinbras.us
134              
135             =head1 CONTRIBUTORS
136              
137             This code owes an intellectual debt to Lincoln Stein. Inelegancies and
138             bugs are mine.
139              
140             Thanks to Barry C. and "Justin Case".
141              
142             =head1 COPYRIGHT AND LICENSE
143              
144             (c) 2009-2017 Mark A. Jensen
145              
146             This program is free software; you can redistribute
147             it and/or modify it under the same terms as Perl itself.
148              
149             The full text of the license can be found in the
150             LICENSE file included with this module.
151              
152             =cut
153              
154             package SQLite_File;
155 6     6   577708 use base qw/Tie::Hash Tie::Array Exporter/;
  6         55  
  6         2699  
156 6     6   11898 use strict;
  6         11  
  6         121  
157 6     6   28 use warnings;
  6         11  
  6         199  
158             our $VERSION = '0.1005';
159              
160 6     6   25 use vars qw( $AUTOLOAD ) ;
  6         11  
  6         244  
161              
162             BEGIN {
163 6 50   6   327 unless (eval "require DBD::SQLite; 1") {
164 0         0 croak( "SQLite_File requires DBD::SQLite" );
165             }
166             }
167              
168 6     6   33 use Fcntl qw(O_CREAT O_RDWR O_RDONLY O_TRUNC);
  6         9  
  6         299  
169 6     6   28 use DBI qw(:sql_types);
  6         12  
  6         1968  
170 6     6   3163 use File::Temp qw( tempfile );
  6         88828  
  6         323  
171 6     6   41 use Carp;
  6         9  
  6         45274  
172              
173             our @EXPORT_OK = qw(
174             $DB_HASH $DB_BTREE $DB_RECNO
175             R_DUP R_CURSOR R_FIRST R_LAST
176             R_NEXT R_PREV R_IAFTER R_IBEFORE
177             R_NOOVERWRITE R_SETCURSOR
178             O_CREAT O_RDWR O_RDONLY O_SVWST
179             O_TRUNC
180             );
181              
182             our $DB_HASH = new SQLite_File::HASHINFO;
183             our $DB_BTREE = new SQLite_File::BTREEINFO;
184             our $DB_RECNO = new SQLite_File::RECNOINFO;
185              
186             # constants hacked out of DB_File:
187 5     5 0 803 sub R_DUP { 32678 }
188 74     74 0 187 sub R_CURSOR { 27 }
189 34     34 0 81 sub R_FIRST { 7 }
190 26     26 0 65 sub R_LAST { 15 }
191 32     32 0 70 sub R_NEXT { 16 }
192 8     8 0 26 sub R_PREV { 23 }
193 4     4 0 10 sub R_IAFTER { 1 }
194 3     3 0 8 sub R_IBEFORE { 3 }
195 2     2 0 5 sub R_NOOVERWRITE { 20 }
196 7     7 0 20 sub R_SETCURSOR { -100 }
197 0     0 0 0 sub O_SVWST { O_CREAT() | O_RDWR() };
198              
199             $SQLite_File::MAXPEND = 250;
200              
201             our $AUTOKEY = 0;
202             # for providing DB_File seq functionality
203             our $AUTOPK = 0;
204              
205             # statement tables
206             our %STMT = (
207             HASH => {
208             put => "INSERT INTO hash (id, obj, pk) VALUES ( ?, ?, ? )",
209             put_seq => "INSERT INTO hash (id, obj, pk) VALUES ( ?, ?, ? )",
210             get => "SELECT obj, pk FROM hash WHERE id = ?",
211             get_seq => "SELECT id, obj FROM hash WHERE pk = ?",
212             upd => "UPDATE hash SET obj = ? WHERE id = ? AND pk = ?",
213             upd_seq => "UPDATE hash SET id = ?, obj = ? WHERE pk = ?",
214             del => "DELETE FROM hash WHERE id = ?",
215             del_seq => "DELETE FROM hash WHERE pk = ?",
216             del_dup => "DELETE FROM hash WHERE id = ? AND obj = ?",
217             sel_dup => "SELECT pk FROM hash WHERE id = ? AND obj = ?",
218             part_seq=> "SELECT id, obj, pk FROM hash WHERE id >= ? LIMIT 1"
219             },
220             ARRAY => {
221             put => "INSERT INTO hash (id, obj) VALUES ( ?, ?)",
222             put_seq => "INSERT INTO hash (obj, id) VALUES ( ?, ?)",
223             get => "SELECT obj, id FROM hash WHERE id = ?",
224             get_seq => "SELECT id, obj FROM hash WHERE id = ?",
225             upd => "UPDATE hash SET obj = ? WHERE id = ?",
226             upd_seq => "UPDATE hash SET obj = ? WHERE id = ?",
227             del => "DELETE FROM hash WHERE id = ?",
228             del_seq => "DELETE FROM hash WHERE id = ?"
229             }
230             );
231              
232             # our own private index
233              
234             sub SEQIDX {
235 249     249 0 304 my $self = shift;
236 249 100       497 return $self->{SEQIDX} = [] if (!defined $self->{SEQIDX});
237 238         512 return $self->{SEQIDX};
238             }
239              
240             sub CURSOR {
241 62     62 0 78 my $self = shift;
242 62         102 return \$self->{CURSOR};
243             }
244              
245             sub TIEHASH {
246 11     11   111552 my $class = shift;
247 11         45 my ($file, $flags, $mode, $index, $keep) = @_;
248 11   100     55 $flags //= O_CREAT|O_RDWR;
249 11         29 my $self = {};
250 11         40 bless($self, $class);
251             # allow $mode to be skipped
252 11 50       46 if (ref($mode) =~ /INFO$/) { # it's the index type
253 0         0 $index = $mode;
254 0         0 $mode = 0644;
255             }
256             #defaults
257 11   100     51 $mode ||= 0644;
258 11   66     123 $index ||= $DB_HASH;
259 11 50 33     178 unless (defined $index and ref($index) =~ /INFO$/) {
260 0         0 croak(__PACKAGE__.": Index type selector must be a HASHINFO, BTREEINFO, or RECNOINFO object");
261             }
262              
263 11         150 $self->{ref} = 'HASH';
264 11         52 $self->{index} = $index;
265 11         25 $self->{pending} = 0;
266 11         23 my ($infix,$fh);
267             # db file handling
268 11 100       41 if ($file) {
269             # you'll love this...
270 8         19 my $setmode;
271 8         24 for ($flags) {
272 8 100       47 $_ eq 'O_SVWST' && do { #bullsith kludge
273 1         9 $_ = 514;
274             };
275 8 100       45 ($_ & O_CREAT) && do {
276 6 100       120 $setmode = 1 if ! -e $file;
277 6 100       54 $infix = (-e $file ? '<' : '>');
278             };
279 8 50       51 ($_ & O_RDWR) && do {
280 8 100       66 $infix = '+'.($infix ? $infix : '<');
281             };
282 8 100       41 ($_ & O_TRUNC) && do {
283 1         9 $infix = '>';
284             };
285 8         30 do { # O_RDONLY
286 8 50       33 $infix = '<' unless $infix;
287             };
288             }
289 8 50       562 open($fh, $infix, $file) or croak(__PACKAGE__.": Can't open db file: $!");
290 8 100       128 chmod $mode, $file if $setmode;
291             # if file explicitly specified, but keep is not,
292             # retain file at destroy...
293 8 100       40 $keep = 1 if !defined $keep;
294             }
295             else {
296             # if no file specified, use a temp file...
297 3         13 ($fh, $file) = tempfile(EXLOCK => 0);
298             # if keep not explicitly specified,
299             # remove the tempfile at destroy...
300 3 50       1158 $keep = 0 if !defined $keep;
301             }
302 11         85 $self->file($file);
303 11         57 $self->_fh($fh);
304 11         45 $self->keep($keep);
305              
306             # create SQL statements
307             my $hash_tbl = sub {
308 11     11   38 my $col = shift;
309 11   100     48 $col ||= 'nocase';
310 11         91 return <
311             (
312             id blob collate $col,
313             obj blob not null,
314             pk integer primary key autoincrement
315             );
316             END
317 11         92 };
318 11         35 my $create_idx = <
319             CREATE INDEX IF NOT EXISTS id_idx ON hash ( id, pk );
320             END
321 11         34 my $dbh = DBI->connect("DBI:SQLite:dbname=".$self->file,"","",
322             {RaiseError => 1, AutoCommit => 0});
323 11         14324 $self->dbh( $dbh );
324             # pragmata inspired by Bio::DB::SeqFeature::Store::DBI::SQLite
325             # $dbh->do("PRAGMA synchronous = OFF");
326 11         101 $dbh->do("PRAGMA temp_store = MEMORY");
327 11   50     2836 $dbh->do("PRAGMA cache_size = ".($index->{cachesize} || 20000));
328              
329 11         1034 for ($index->{'type'}) {
330 11   100     72 my $flags = $index->{flags} || 0;
331 11 50       51 !defined && do {
332 0         0 $self->dbh->do("CREATE TABLE IF NOT EXISTS hash $hash_tbl");
333 0         0 last;
334             };
335 11 100       62 $_ eq 'BINARY' && do {
336 4         9 my $col = 'nocase';
337 4 100       11 if (ref($index->{'compare'}) eq 'CODE') {
338 2         7 $self->dbh->func( 'usr', $index->{'compare'}, "create_collation");
339 2         36 $col = 'usr';
340             }
341 4 100       11 if ($flags & R_DUP ) {
342 3         11 $self->dup(1);
343 3         8 $self->dbh->do("CREATE TABLE IF NOT EXISTS hash ".$hash_tbl->($col));
344 3         657 $self->dbh->do($create_idx);
345             }
346             else {
347 1         7 $self->dup(0);
348 1         4 $self->dbh->do("CREATE TABLE IF NOT EXISTS hash ".$hash_tbl->($col));
349 1         288 $self->dbh->do($create_idx);
350             }
351 4         568 last;
352             };
353 7 50       27 $_ eq 'HASH' && do {
354 7         26 $self->dbh->do("CREATE TABLE IF NOT EXISTS hash ".$hash_tbl->());
355 7         1872 $self->dbh->do($create_idx);
356 7         978 last;
357             };
358 0 0       0 $_ eq 'RECNO' && do {
359 0         0 croak(__PACKAGE__.": \$DB_RECNO is not meaningful for tied hashes");
360 0         0 last;
361             };
362 0         0 do {
363 0         0 croak(__PACKAGE__.": Index type not defined or not recognized");
364             };
365             }
366 11 50 100     105 $self->_index if ($infix and $infix =~ /{type} eq 'BINARY');
      66        
367 11         69 $self->commit(1);
368             # barryc fix : fast forward the autokey
369 11         51 my ($sth)=$self->dbh->prepare("select max(pk) from hash");
370 11         1533 $sth->execute();
371 11         131 ($AUTOPK)=$sth->fetchrow_array();
372 11         286 return $self;
373             }
374              
375             sub TIEARRAY {
376 3     3   3800 my $class = shift;
377 3         11 my ($file, $flags, $mode, $index, $keep) = @_;
378 3   100     15 $flags //= O_CREAT|O_RDWR;
379 3         8 my $self = {};
380 3         7 bless($self, $class);
381              
382 3         14 $self->{ref} = 'ARRAY';
383             # allow $mode to be skipped
384 3 50       12 if (ref($mode) =~ /INFO$/) { # it's the index type
385 0         0 $index = $mode;
386 0         0 $mode = 0644;
387             }
388 3   100     13 $mode ||= 0644;
389 3   66     30 $index ||= $DB_RECNO;
390 3 50 33     39 unless (defined $index and ref($index) =~ /INFO$/) {
391 0         0 croak(__PACKAGE__.": Index type selector must be a HASHINFO, BTREEINFO, or RECNOINFO object");
392             }
393             croak(__PACKAGE__.": Arrays must be tied to type RECNO") unless
394 3 50       15 $index->{type} eq 'RECNO';
395 3         6 $self->{index} = $index;
396 3         5 $self->{pending} = 0;
397 3         6 my ($infix,$fh);
398             # db file handling
399 3 100       8 if ($file) {
400 1         2 my $setmode;
401 1         2 for ($flags) {
402 1 50       3 $_ eq 'O_SVWST' && do { #bullsith kludge
403 0         0 $_ = 514;
404             };
405 1 50       3 ($_ & O_CREAT) && do {
406 1 50       14 $setmode = 1 if ! -e $file;
407 1 50       14 $infix = (-e $file ? '<' : '>');
408             };
409 1 50       4 ($_ & O_RDWR) && do {
410 1 50       3 $infix = '+'.($infix ? $infix : '<');
411             };
412 1 50       4 ($_ & O_TRUNC) && do {
413 0         0 $infix = '>';
414             };
415 1         2 do { # O_RDONLY
416 1 50       3 $infix = '<' unless $infix;
417             };
418             }
419 1 50       34 open($fh, $infix, $file) or croak(__PACKAGE__.": Can't open db file: $!");
420 1 50       4 chmod $mode, $file if $setmode;
421             # if file explicitly specified, but keep is not,
422             # retain file at destroy...
423 1 50       3 $keep = 1 if !defined $keep;
424             }
425             else {
426             # if no file specified, use a temp file...
427 2         15 ($fh, $file) = tempfile(EXLOCK => 0);
428             # if keep not explicitly specified,
429             # remove the tempfile at destroy...
430 2 50       876 $keep = 0 if !defined $keep;
431             }
432 3         13 $self->file($file);
433 3         8 $self->_fh($fh);
434 3         9 $self->keep($keep);
435            
436 3         7 my $arr_tbl = <
437             (
438             id integer primary key,
439             obj blob not null
440             );
441             END
442            
443 3         6 my $create_idx = <
444             CREATE INDEX IF NOT EXISTS id_idx ON hash ( id );
445             END
446            
447 3         8 my $dbh = DBI->connect("dbi:SQLite:dbname=".$self->file,"","",
448             {RaiseError => 1, AutoCommit => 0});
449 3         1029 $self->dbh( $dbh );
450              
451 3         8 for ($index->{'type'}) {
452 3   50     15 my $flags = $index->{flags} || 0;
453 3 50       13 $_ eq 'BINARY' && do {
454 0         0 $self->dbh->disconnect;
455 0         0 croak(__PACKAGE__.": \$DB_BTREE is not meaningful for a tied array");
456 0         0 last;
457             };
458 3 50       10 $_ eq 'HASH' && do {
459 0         0 $self->dbh->disconnect;
460 0         0 croak(__PACKAGE__.": \$DB_HASH is not meaningful for a tied array");
461 0         0 last;
462             };
463 3 50       20 $_ eq 'RECNO' && do {
464 3         7 $self->dbh->do("CREATE TABLE IF NOT EXISTS hash $arr_tbl");
465 3         1332 $self->dbh->do($create_idx);
466 3         368 my $r = $self->dbh->selectall_arrayref("select * from hash");
467 3         192 for (@$r) {
468 3         4 push @{$self->SEQIDX},$$_[0];
  3         5  
469             }
470 3         8 last;
471             };
472 0         0 do {
473 0         0 croak(__PACKAGE__.": Index type not defined or not recognized");
474             };
475             }
476 3         13 $self->commit(1);
477 3         36 return $self;
478             }
479              
480             # common methods for hashes and arrays
481              
482             sub FETCH {
483 60     60   378 my $self = shift;
484 60         84 my $key = shift;
485 60         66 my $fkey;
486 60 50       123 return unless $self->dbh;
487 60         133 $self->commit;
488 60 100 66     167 if (!$self->{ref} or $self->ref eq 'HASH') {
    50          
489 35         60 local $_ = $key;
490 35         86 $self->_store_key_filter;
491 35         179 $self->get_sth->execute($_); # fetches on column 'id'
492             }
493             elsif ($self->ref eq 'ARRAY') {
494 25 100       27 if (defined ${$self->SEQIDX}[$key]) {
  25         30  
495 24         89 $self->get_sth->execute($self->get_idx($key));
496             }
497             else {
498 1         3 $self->_last_pk(undef);
499 1         2 return undef;
500             }
501             }
502             else { # type not recognized
503 0         0 croak(__PACKAGE__.": tied type not recognized");
504             }
505 59         327 my $ret = $self->get_sth->fetch;
506 59 100       164 if ($ret) {
507 56         139 $self->_last_pk( $ret->[1] ); # store the returned pk
508 56         96 $ret->[0] =~ s{}{'}g;
509 56         80 $ret->[0] =~ s{}{"}g;
510 56         89 local $_ = $ret->[0];
511 56         140 $self->_fetch_value_filter;
512 56         258 return $_; # always returns the object
513             }
514             else {
515 3         11 $self->_last_pk( undef ); # fail in pk
516 3         9 return $ret;
517             }
518             }
519              
520             sub STORE {
521 49     49   574 my $self = shift;
522 49         111 my ($key, $value) = @_;
523 49         69 my ($fkey, $fvalue);
524 49 50       101 return unless $self->dbh;
525             {
526             # filter value
527 49         73 local $_ = $value;
528 49         116 $self->_store_value_filter;
529 49         106 $fvalue = $_;
530             }
531             {
532             # filter key
533 49         67 $_ = $key;
  49         59  
  49         58  
534 49         108 $self->_store_key_filter;
535 49         95 $fkey = $_;
536             }
537 49         145 $fvalue =~ s{'}{}g;
538 49         62 $fvalue =~ s{"}{}g;
539 49         63 my ($pk, $sth);
540 49 100 66     164 if ( !defined $self->{ref} or $self->ref eq 'HASH' ) {
    50          
541 36 100       102 if ( $self->dup ) { # allowing duplicates
542 17         41 $pk = $self->_get_pk;
543 17         78 $sth = $self->put_sth;
544 17         80 $sth->bind_param(1,$fkey);
545 17         67 $sth->bind_param(2,$fvalue, SQL_BLOB);
546 17         47 $sth->bind_param(3,$pk);
547 17         61 $self->put_sth->execute();
548 17         138 push @{$self->SEQIDX}, $pk;
  17         36  
549             }
550             else { # no duplicates...
551             #need to check if key is already present
552 19 100       70 if ( $self->EXISTS($key) )
553             {
554 1         12 $sth = $self->upd_sth;
555 1         13 $sth->bind_param(1,$fvalue, SQL_BLOB);
556 1         4 $sth->bind_param(2,$key);
557 1         5 $sth->bind_param(3,$self->_last_pk);
558 1         251 $sth->execute();
559             }
560             else {
561 18         58 $pk = $self->_get_pk;
562 18         89 $sth = $self->put_sth;
563 18         104 $sth->bind_param(1,$fkey);
564 18         75 $sth->bind_param(2,$fvalue, SQL_BLOB);
565 18         49 $sth->bind_param(3,$pk);
566 18         972 $sth->execute();
567 18         37 push @{$self->SEQIDX}, $pk;
  18         61  
568             }
569             }
570 36         82 $self->{_stale} = 1;
571             }
572             elsif ( $self->ref eq 'ARRAY' ) {
573             # need to check if the key is already present
574 13 100       14 if (!defined ${$self->SEQIDX}[$key] ) {
  13         20  
575 12         49 $self->put_sth->execute($self->get_idx($key), $fvalue);
576             }
577             else {
578 1         22 $self->upd_sth->execute($fvalue,$self->get_idx($key));
579             }
580             }
581 49         93 ++$self->{pending};
582 49         205 $value;
583             }
584              
585             sub DELETE {
586 3     3   7 my $self = shift;
587 3         6 my $key = shift;
588 3 50       8 return unless $self->dbh;
589 3         6 my $fkey;
590             { # filter key
591 3         5 local $_ = $key;
  3         6  
592 3         8 $self->_store_key_filter;
593 3         5 $fkey = $_;
594             }
595 3 50 66     10 $self->_reindex if ($self->index->{type} eq 'BINARY' and $self->_index_is_stale);
596 3         5 my $oldval;
597 3 100 66     9 if (!$self->ref or $self->ref eq 'HASH') {
    50          
598 2 50       11 return unless $self->get_sth->execute($fkey);
599 2         11 my $ret = $self->get_sth->fetch;
600 2         6 $oldval = $ret->[0];
601 2         16 $self->del_sth->execute($fkey); # del on id
602             # update the sequential side
603 2 50       8 if ($ret->[1]) {
604 2         3 delete ${$self->SEQIDX}[_find_idx($ret->[1],$self->SEQIDX)];
  2         8  
605             }
606             }
607             elsif ($self->ref eq 'ARRAY') {
608 1         6 my $SEQIDX = $self->SEQIDX;
609 1 50       5 if ($$SEQIDX[$key]) {
610 1         3 $oldval = $self->FETCH($$SEQIDX[$key]);
611             # $self->dbh->do("DELETE FROM hash WHERE id = '$$SEQIDX[$key]'");
612 1         5 $self->del_sth->execute($$SEQIDX[$key]); # del on id
613 1         11 $self->rm_idx($key);
614             }
615             }
616             else {
617 0         0 croak( __PACKAGE__.": tied type not recognized" );
618             }
619 3         8 ++$self->{pending};
620 3         8 $_ = $oldval;
621 3         7 $self->_fetch_value_filter;
622 3         17 return $_;
623             }
624              
625             sub EXISTS {
626 25     25   76 my $self = shift;
627 25         33 my $key = shift;
628 25 50       47 return unless $self->dbh;
629              
630 25         63 $self->commit;
631 25 50 33     66 if (!$self->ref or $self->ref eq 'HASH') {
    0          
632 25         55 local $_ = $key;
633 25         52 $self->_store_key_filter;
634 25         166 $self->get_sth->execute($_);
635 25         113 my $ret = $self->get_sth->fetch;
636 25 100       109 return $self->_last_pk(defined($ret) ? $ret->[1] : undef);
637             }
638             elsif ($self->ref eq 'ARRAY') {
639 0         0 return $self->_last_pk(${$self->SEQIDX}[$key]);
  0         0  
640             }
641             else {
642 0         0 croak(__PACKAGE__.": tied type not recognized");
643             }
644             }
645              
646             sub CLEAR {
647 2     2   5 my $self = shift;
648 2 50       6 return unless $self->dbh;
649 2         7 $self->dbh->commit;
650 2         4 my $sth = $self->dbh->prepare("DELETE FROM hash");
651 2         434 $sth->execute;
652 2         11 $self->dbh->commit;
653 2         11 @{$self->SEQIDX} = ();
  2         11  
654 2         43 return 1;
655             }
656              
657             # hash methods
658              
659             sub FIRSTKEY {
660 6     6   218 my $self = shift;
661 6 50       21 return unless $self->dbh;
662 6         24 $self->commit;
663 6 50 33     26 return if ($self->{ref} and $self->ref ne 'HASH');
664 6         14 my $ids = $self->dbh->selectall_arrayref("SELECT id FROM hash");
665 6 50       596 return unless $ids;
666 6         20 $ids = [ map { $_->[0] } @$ids ];
  27         65  
667             { # filter keys
668 6         14 $self->_fetch_key_filter for (@$ids);
  6         45  
669             }
670 6         21 return $self->_keys($ids);
671             }
672              
673             sub NEXTKEY {
674 27     27   53 my $self = shift;
675 27         34 my $lastkey = shift;
676 27 50       47 return unless $self->dbh;
677 27 50 33     68 return if ($self->{ref} and $self->ref ne 'HASH');
678 27         47 return $self->_keys;
679             }
680              
681             # array methods
682              
683             sub FETCHSIZE {
684 25     25   750 my $self = shift;
685 25 50       41 return unless $self->dbh;
686 25 50 33     65 return if (!$self->{ref} or $self->ref ne 'ARRAY');
687 25         48 $self->len;
688             }
689              
690             sub STORESIZE {
691 0     0   0 my $self = shift;
692 0         0 my $count = shift;
693 0 0       0 return unless $self->dbh;
694 0 0 0     0 return if (!$self->ref or $self->ref ne 'ARRAY');
695 0 0       0 if ($count > $self->len) {
    0          
696 0         0 foreach ($count - $self->len .. $count) {
697 0         0 $self->STORE($_, '');
698             }
699             }
700             elsif ($count < $self->len) {
701 0         0 foreach (0 .. $self->len - $count - 2) {
702 0         0 $self->POP();
703             }
704             }
705             }
706              
707             # EXTEND is no-op
708             sub EXTEND {
709 2     2   4 my $self = shift;
710 2         4 my $count = shift;
711 2         10 return;
712             }
713              
714             sub POP {
715 3     3   8 my $self = shift;
716 3 50       7 return unless $self->dbh;
717 3         7 $self->commit;
718 3 50 33     8 return if (!$self->{ref} or $self->ref ne 'ARRAY');
719 3         11 $self->get_sth->execute($self->get_idx($self->len-1));
720 3         13 my $ret = $self->get_sth->fetch;
721             # $self->dbh->do("DELETE FROM hash WHERE id = ".$self->get_idx($self->len-1));
722 3         11 $self->del_sth->execute($self->get_idx($self->len-1));
723             # bookkeeping
724 3         9 $self->rm_idx($self->len-1);
725 3 50       12 return defined $ret ? $ret->[0] : $ret;
726             }
727              
728             sub PUSH {
729 2     2   5 my $self = shift;
730 2         4 my @values = @_;
731 2 50       6 return unless $self->dbh;
732 2 50 33     7 return if (!$self->{ref} or $self->ref ne 'ARRAY');
733 2         6 my $ret = @values;
734 2         5 my $beg = $self->len;
735 2         7 my $end = $self->len + @values - 1;
736 2         6 for my $i ($beg..$end) {
737 4         15 $self->put_sth->execute($self->get_idx($i), shift @values);
738             }
739 2         7 ++$self->{pending};
740 2         9 return $ret;
741             }
742              
743             sub SHIFT {
744 3     3   8 my $self = shift;
745 3 50       8 return unless $self->dbh;
746 3         8 $self->commit;
747 3 50 33     10 return if (!$self->{ref} or $self->ref ne 'ARRAY');
748 3         11 $self->get_sth->execute( $self->get_idx(0) );
749 3         12 my $ret = $self->get_sth->fetch;
750 3         10 $self->del_sth->execute($self->get_idx(0));
751             # bookkeeping
752 3         21 $self->shift_idx;
753 3   33     11 $_ = $ret && $ret->[0];
754 3         7 $self->_fetch_value_filter;
755 3         10 return $_;
756             }
757              
758             sub UNSHIFT {
759 2     2   4 my $self = shift;
760 2         5 my @values = @_;
761 2 50 33     10 return if (!$self->{ref} or $self->ref ne 'ARRAY');
762 2         4 my $n = @values;
763 2         6 $self->_store_value_filter for @values;
764 2 50       5 return unless $self->dbh;
765 2         7 for ($self->unshift_idx($n)) {
766 3         13 $self->put_sth->execute($_,shift @values);
767             }
768 2         5 ++$self->{pending};
769 2         6 return $n;
770             }
771              
772             sub SPLICE {
773 1     1   450 my $self = shift;
774 1   50     4 my $offset = shift || 0;
775 1   33     3 my $length = shift || $self->FETCHSIZE() - $offset;
776 1         3 my @list = @_;
777 1         3 my $SEQIDX = $self->SEQIDX;
778 1         4 $self->_wring_SEQIDX;
779 1         3 my @pk = map { $self->get_idx($_)} ($offset..$offset+$length-1);
  2         5  
780 1         9 my @ret;
781 1         3 for (@pk) {
782 2         9 $self->get_sth->execute($_);
783 2         4 push @ret, ${$self->get_sth->fetch}[0];
  2         6  
784 2         7 $self->del_sth->execute($_);
785             }
786 1         4 my @new_idx = map { $AUTOKEY++ } @list;
  3         6  
787 1         3 splice( @$SEQIDX, $offset, $length, @new_idx );
788 1         5 $self->put_sth->execute($_, shift @list) for @new_idx;
789 1         5 $self->_fetch_value_filter for @ret;
790 1         7 return @ret;
791             }
792              
793             # destructors
794              
795             sub UNTIE {
796 8     8   566 my $self = shift;
797 8         15 my $count = shift;
798              
799 8 50       33 croak( __PACKAGE__.": untie attempted while $count inner references still exist" ) if ($count);}
800              
801             sub DESTROY {
802 14     14   1555 my $self = shift;
803 14         39 $self->dbh->commit; #'hard' commit
804 14         130 my $tbl = $STMT{$self->ref};
805             # finish and destroy stmt handles
806 14         92 for ( keys %$tbl ) {
807 145 100       482 $self->{$_."_sth"}->finish if $self->{$_."_sth"};
808 145         660 undef $self->{$_."_sth"};
809             }
810             # disconnect
811 14 50       59 croak($self->dbh->errstr) unless $self->dbh->disconnect;
812 14         121 $self->{dbh}->DESTROY;
813 14         452 undef $self->{dbh};
814             # remove file if nec
815 14 50       49 $self->_fh->close() if $self->_fh;
816 14 100       347 if (-e $self->file) {
817 13         113 local $!;
818 13 100 66     41 unlink $self->file if (!$self->keep && $self->_fh);
819 13 50       87 $! && carp(__PACKAGE__.": unlink issue: $!");
820             }
821 14         33 undef $self;
822 14         577 1;
823             }
824              
825             # dbm filter storage hooks
826              
827             sub filter_store_key {
828 4     4 0 165 my $self = shift;
829 4         7 my $code = shift;
830            
831 4 50 66     31 unless (!defined($code) or ref($code) eq 'CODE') {
832 0         0 croak(__PACKAGE__."::filter_store_key requires a coderef argument");
833             }
834 4         16 $self->_store_key_filter($code);
835            
836             };
837              
838             sub filter_store_value {
839 2     2 0 17 my $self = shift;
840 2         4 my $code = shift;
841 2 50 33     15 unless (!defined($code) or ref($code) eq 'CODE') {
842 0         0 croak(__PACKAGE__."::filter_store_value requires a coderef argument");
843             }
844 2         11 $self->_store_value_filter($code);
845            
846             };
847              
848             sub filter_fetch_key {
849 3     3 0 544 my $self = shift;
850 3         5 my $code = shift;
851 3 100 66     18 unless (!defined($code) or ref($code) eq 'CODE') {
852 1         18 croak(__PACKAGE__."::filter_fetch_key requires a coderef argument");
853             }
854 2         13 $self->_fetch_key_filter($code);
855             };
856              
857             sub filter_fetch_value {
858 2     2 0 16 my $self = shift;
859 2         5 my $code = shift;
860 2 50 33     15 unless (!defined($code) or ref($code) eq 'CODE') {
861 0         0 croak(__PACKAGE__."::filter_fetch_value requires a coderef argument");
862             }
863 2         8 $self->_fetch_value_filter($code);
864             };
865              
866             # filters
867              
868             sub _fetch_key_filter {
869 29     29   58 my $self = shift;
870 29 100       61 if (@_) {
871 2         11 $self->{_fetch_key_filter} = shift;
872 2         6 return 1;
873             }
874 27 100       71 return unless defined $self->{_fetch_key_filter};
875 6         7 &{$self->{_fetch_key_filter}};
  6         9  
876             };
877              
878             sub _fetch_value_filter {
879 66     66   82 my $self = shift;
880 66 100       127 if (@_) {
881 2         4 $self->{_fetch_value_filter} = shift;
882 2         7 return 1;
883             }
884 64 100       131 return unless defined $self->{_fetch_value_filter};
885 7         15 &{$self->{_fetch_value_filter}};
  7         17  
886             };
887              
888             sub _store_key_filter {
889 116     116   142 my $self = shift;
890 116 100       208 if (@_) {
891 4         16 $self->{_store_key_filter} = shift;
892 4         13 return 1;
893             }
894 112 100       201 return unless defined $self->{_store_key_filter};
895 15         22 &{$self->{_store_key_filter}};
  15         41  
896             };
897              
898             sub _store_value_filter {
899 54     54   72 my $self = shift;
900 54 100       90 if (@_) {
901 2         5 $self->{_store_value_filter} = shift;
902 2         57 return 1;
903             }
904 52 100       108 return unless defined $self->{_store_value_filter};
905 7         14 &{$self->{_store_value_filter}};
  7         17  
906             };
907              
908             =head1 Attribute Accessors
909              
910             =head2 file()
911              
912             Title : file
913             Usage : $db->file()
914             Function: filename for the SQLite db
915             Example :
916             Returns : value of file (a scalar)
917             Args :
918              
919             =cut
920              
921             sub file {
922 50     50 1 90 my $self = shift;
923            
924 50 100       132 return $self->{'file'} = shift if @_;
925 36         744 return $self->{'file'};
926             }
927              
928             =head2 _fh()
929              
930             Title : _fh
931             Usage : $db->_fh()
932             Function: holds the temp file handle
933             Example :
934             Returns : value of _fh (a scalar)
935             Args :
936              
937             =cut
938              
939             sub _fh {
940 49     49   78 my $self = shift;
941            
942 49 100       112 return $self->{'_fh'} = shift if @_;
943 35         274 return $self->{'_fh'};
944             }
945              
946             =head2 keep()
947              
948             Title : keep
949             Usage : $db->keep()
950             Function: flag allows preservation of db file when set
951             Returns : value of keep (a scalar)
952             Args :
953              
954             =cut
955              
956             sub keep {
957 28     28 1 52 my $self = shift;
958            
959 28 100       77 return $self->{'keep'} = shift if @_;
960 13         61 return $self->{'keep'};
961             }
962              
963             =head2 ref()
964              
965             Title : ref
966             Usage : $db->ref
967             Function: HASH or ARRAY? Find out.
968             Returns : scalar string : 'HASH' or 'ARRAY'
969             Args : none
970              
971             =cut
972              
973              
974             sub ref {
975 1031     1031 1 1590 my $self = shift;
976 1031         6111 return $self->{ref};
977             }
978              
979             =head2 index()
980              
981             Title : index
982             Usage : $db->index()
983             Function: access the index type structure ($DB_BTREE, $DB_HASH,
984             $DB_RECNO) that initialized this instance
985             Returns : value of index (a hashref)
986             Args :
987              
988             =cut
989              
990             sub index {
991 54     54 1 62 my $self = shift;
992 54         176 return $self->{'index'};
993             }
994              
995             # =head2 _keys
996              
997             # Title : _keys
998             # Usage : internal
999             # Function: points to a hash to make iterating easy and fun
1000             # Example :
1001             # Returns : value of _keys (a hashref)
1002             # Args : on set, an arrayref of scalar keys
1003              
1004             # =cut
1005              
1006             sub _keys {
1007 33     33   48 my $self = shift;
1008 33         46 my $load = shift;
1009 33 100       62 if ($load) {
1010 6         19 $self->{'_keys'} = {};
1011 6         20 @{$self->{'_keys'}}{ @$load } = (undef) x @$load;
  6         28  
1012 6         8 my $a = keys %{$self->{'_keys'}}; #reset each
  6         19  
1013             }
1014 33         46 return each %{$self->{'_keys'}};
  33         179  
1015             }
1016              
1017             =head1 BDB API Emulation : random access
1018              
1019             =head2 get()
1020              
1021             Title : get
1022             Usage : $db->get($key, $value)
1023             Function: Get value associated with key
1024             Returns : 0 on success, 1 on fail;
1025             value in $value
1026             Args : as in DB_File
1027              
1028             =cut
1029              
1030             sub get {
1031 2     2 1 5 my $self = shift;
1032 2         5 my ($key, $value) = @_;
1033 2 50       5 return unless $self->dbh;
1034 2 100       6 $_[1] = ($self->ref eq 'ARRAY' ? $self->FETCH(${$self->SEQIDX}[$key]) : $self->FETCH($key));
  1         3  
1035 2 50       11 return 0 if defined $_[1];
1036 0         0 return 1;
1037             }
1038              
1039             =head2 put()
1040              
1041             Title : put
1042             Usage : $db->put($key, $value, $flags)
1043             Function: Replace a key's value, or
1044             put a key-value pair
1045             Returns : 0 on success, 1 on fail;
1046             value in $value
1047             key in $key if $flags == R_CURSOR
1048             Args : as in DB_File
1049              
1050             =cut
1051              
1052             sub put {
1053 8     8 1 24 my $self = shift;
1054 8         17 my ($key, $value, $flags) = @_;
1055 8 50       16 return unless $self->dbh;
1056              
1057 8         14 my $SEQIDX = $self->SEQIDX;
1058 8         15 my $CURSOR = $self->CURSOR;
1059 8         18 my ($status, $pk, @parms);
1060 8         0 my ($sth, $do_cursor);
1061 8         15 for ($flags) {
1062 8 100 100     26 (!defined || $_ == R_SETCURSOR) && do { # put or upd
1063 5 50       14 if ($self->dup) { # just make a new one
1064 0         0 $pk = $self->_get_pk;
1065 0         0 $sth = $self->put_seq_sth;
1066             $do_cursor = sub {
1067 0     0   0 push @$SEQIDX, $pk;
1068 0 0       0 $$CURSOR = $#$SEQIDX if $flags;
1069 0 0       0 $self->_reindex if $self->index->{type} eq 'BINARY';
1070 0         0 };
1071             }
1072             else {
1073 5         19 $self->FETCH($key);
1074 5   66     9 $pk = $self->_last_pk || $self->_get_pk;
1075 5 100       9 $sth = ($self->_last_pk ?
1076             $self->upd_seq_sth :
1077             $self->put_seq_sth);
1078             $do_cursor = sub {
1079 5 100   5   11 push @$SEQIDX, $pk if !$self->_last_pk;
1080 5 100       11 $flags && do { # R_SETCURSOR
1081 2 50       6 if ( $pk = $$SEQIDX[-1] ) {
1082 2         4 $$CURSOR = $#$SEQIDX;
1083             }
1084             else {
1085 0         0 $$CURSOR = _find_idx($pk, $SEQIDX);
1086             };
1087 2 100       3 $self->_reindex if $self->index->{type} eq 'BINARY';
1088             };
1089 5         35 };
1090             }
1091 5         11 last;
1092             };
1093 3 100       7 $_ == R_IAFTER && do {
1094 1 50       4 $self->_wring_SEQIDX unless $$SEQIDX[$$CURSOR];
1095             # duplicate protect
1096 1 0 33     3 return 1 unless ($self->ref eq 'ARRAY') || $self->dup || !$self->EXISTS($key);
      33        
1097             croak(__PACKAGE__.": R_IAFTER flag meaningful only for RECNO type") unless
1098 1 50       2 $self->index->{type} eq 'RECNO';
1099 1         2 $pk = $self->_get_pk;
1100 1         5 $sth = $self->put_seq_sth;
1101 1         4 $_[0] = $$CURSOR+1;
1102             $do_cursor = sub {
1103 1 50   1   4 if ($$CURSOR == $#$SEQIDX) {
1104 1         3 push @$SEQIDX, $pk;
1105             }
1106             else {
1107 0         0 splice(@$SEQIDX,$$CURSOR,0,$pk);
1108             }
1109 1         7 };
1110 1         2 last;
1111             };
1112 2 100       4 $_ == R_IBEFORE && do {
1113 1 50       6 $self->_wring_SEQIDX unless $$SEQIDX[$$CURSOR];
1114             # duplicate protect
1115 1 0 33     3 return 1 unless ($self->ref eq 'ARRAY') || $self->dup || !$self->EXISTS($key);
      33        
1116             croak(__PACKAGE__.": R_IBEFORE flag meaningful only for RECNO type") unless
1117 1 50       3 $self->index->{type} eq 'RECNO';
1118 1         3 $pk = $self->_get_pk;
1119 1         5 $sth = $self->put_seq_sth;
1120 1         4 $_[0] = $$CURSOR;
1121             $do_cursor = sub {
1122 1 50   1   4 if ($$CURSOR) {
1123 0         0 splice(@$SEQIDX,$$CURSOR-1,0,$pk);
1124             }
1125             else {
1126 1         3 unshift(@$SEQIDX, $pk);
1127             }
1128 1         29 $$CURSOR++; # preserve cursor
1129 1         10 };
1130 1         3 last;
1131             };
1132 1 50       3 $_ == R_CURSOR && do { # upd only
1133 0 0       0 $self->_wring_SEQIDX unless $$SEQIDX[$$CURSOR];
1134             # duplicate protect
1135 0 0 0     0 return 1 unless ($self->ref eq 'ARRAY') || $self->dup || !$self->EXISTS($key);
      0        
1136 0         0 $pk = $$SEQIDX[$$CURSOR];
1137 0         0 $sth = $self->upd_seq_sth;
1138             $do_cursor = sub {
1139 0 0   0   0 $self->_reindex if $self->index->{type} eq 'BINARY';
1140 0         0 };
1141 0         0 last;
1142             };
1143 1 50       3 $_ == R_NOOVERWRITE && do { # put only/add to the "end"
1144             #will create a duplicate if $self->dup is set!
1145 1 50 33     3 return 1 unless ($self->ref eq 'ARRAY') || $self->dup || !$self->EXISTS($key);
      33        
1146 0         0 $pk = $self->_get_pk;
1147 0         0 $sth = $self->put_seq_sth;
1148             $do_cursor = sub {
1149 0     0   0 push @$SEQIDX, $pk;
1150 0 0       0 $self->_reindex if $self->index->{type} eq 'BINARY';
1151 0         0 };
1152 0         0 last;
1153             };
1154             }
1155 7 100       19 if ($self->ref eq 'ARRAY') {
1156 4         27 $sth->bind_param(1, $value, SQL_BLOB);
1157 4         11 $sth->bind_param(2, $pk);
1158             }
1159             else {
1160 3         15 $sth->bind_param(1, $key);
1161 3         11 $sth->bind_param(2, $value, SQL_BLOB);
1162 3         6 $sth->bind_param(3, $pk);
1163             }
1164 7         156 $status = !$sth->execute;
1165 7 50       25 $do_cursor->() if !$status;
1166 7         12 $self->{pending} = 1;
1167 7 100       19 $self->{_stale} = 0 if $self->index->{type} eq 'BINARY';
1168 7         59 return $status;
1169             }
1170              
1171             =head2 del()
1172              
1173             Title : del
1174             Usage : $db->del($key)
1175             Function: delete key-value pairs corresponding to $key
1176             Returns : 0 on success, 1 on fail
1177             Args : as in DB_File
1178              
1179             =cut
1180              
1181             sub del {
1182 2     2 1 4 my $self = shift;
1183 2         4 my ($key, $flags) = @_;
1184 2 50       6 return unless $self->dbh;
1185 2 50 66     6 $self->_reindex if ($self->index->{type} eq 'BINARY' and $self->_index_is_stale);
1186 2         5 my $SEQIDX = $self->SEQIDX;
1187 2         5 my $CURSOR = $self->CURSOR;
1188 2         3 my $status;
1189 2 50       5 if ($flags eq R_CURSOR) {
1190 2 50       7 _wring_SEQIDX($self->SEQIDX) unless $$SEQIDX[$$CURSOR];
1191 2         4 my $pk = $$SEQIDX[$$CURSOR];
1192 2         12 $status = $self->del_seq_sth->execute($pk);
1193 2 50       9 if ($status) { # successful delete
1194 2         4 $$SEQIDX[$$CURSOR] = undef;
1195 2         9 $self->_wring_SEQIDX;
1196             }
1197 2         3 1;
1198             }
1199             else {
1200             # delete all matches
1201 0         0 $status = $self->DELETE($key);
1202 0         0 1;
1203             }
1204 2         6 $self->{_stale} = 1;
1205 2         4 $self->{pending} = 1;
1206 2 50       11 return 0 if $status;
1207 0         0 return 1;
1208             }
1209              
1210             =head1 BDB API Emulation : sequential access
1211              
1212             =head2 seq()
1213              
1214             Title : seq
1215             Usage : $db->seq($key, $value, $flags)
1216             Function: retrieve key-value pairs sequentially,
1217             according to $flags, with partial matching
1218             on $key; see DB_File
1219             Returns : 0 on success, 1 on fail;
1220             key in $key,
1221             value in $value
1222             Args : as in DB_File
1223              
1224             =cut
1225              
1226             sub seq {
1227 34     34 1 50 my $self = shift;
1228 34         67 my ($key, $value, $flags) = @_;
1229 34 50       62 return 1 unless $flags;
1230 34         78 $self->commit;
1231 34         41 my $status;
1232 34 100 100     55 $self->_reindex if ($self->index->{type} eq 'BINARY' and $self->_index_is_stale);
1233 34         59 my $SEQIDX = $self->SEQIDX;
1234 34         54 my $CURSOR = $self->CURSOR;
1235 34         59 for ($flags) {
1236 34 100       62 $_ eq R_CURSOR && do {
1237 6         11 last;
1238             };
1239 28 100       60 $_ eq R_FIRST && do {
1240 6         9 $$CURSOR = 0;
1241 6         8 last;
1242             };
1243 22 100       39 $_ eq R_LAST && do {
1244 4         15 $$CURSOR = $#$SEQIDX;
1245 4         9 last;
1246             };
1247 18 100       62 $_ eq R_NEXT && do {
1248 14 100       44 return 1 if ($$CURSOR >= $#$SEQIDX);
1249 11         15 ($$CURSOR)++;
1250 11         15 last;
1251             };
1252 4 50       35 $_ eq R_PREV && do {
1253 4 100       15 return 1 if $$CURSOR == 0;
1254 2         3 ($$CURSOR)--;
1255 2         3 last;
1256             };
1257             }
1258 29 100       70 $self->_wring_SEQIDX() unless defined $$SEQIDX[$$CURSOR];
1259             # get by pk, set key and value.
1260 29 100 100     41 if (($flags == R_CURSOR ) && $self->ref eq 'HASH') {
1261 3         19 $status = $self->partial_match($key, $value);
1262 3         5 $_[0] = $key; $_[1] = $value;
  3         25  
1263 3         13 return $status;
1264             }
1265             else {
1266 26         120 $self->get_seq_sth->execute($$SEQIDX[$$CURSOR]);
1267 26         106 my $ret = $self->get_seq_sth->fetch;
1268 26 100       55 ($_[0], $_[1]) = (($self->ref eq 'ARRAY' ? $$CURSOR : $$ret[0]), $$ret[1]);
1269             }
1270 26         153 return 0;
1271             }
1272              
1273             =head2 sync()
1274              
1275             Title : sync
1276             Usage : $db->sync
1277             Function: stub for BDB sync
1278             Returns : 0
1279             Args : none
1280              
1281             =cut
1282              
1283 0     0 1 0 sub sync { !shift->commit };
1284              
1285             =head1 BDB API Emulation : C
1286              
1287             =head2 dup
1288              
1289             Title : dup
1290             Usage : $db->dup()
1291             Function: Get/set flag indicating whether duplicate keys
1292             are preserved
1293             Returns : boolean
1294             Args : [optional] on set, new value (a scalar or undef, optional)
1295              
1296             =cut
1297              
1298             sub dup {
1299 50     50 1 70 my $self = shift;
1300 50 100       146 return $self->{'dup'} = shift if @_;
1301 46         129 return $self->{'dup'};
1302             }
1303              
1304             =head2 get_dup()
1305              
1306             Title : get_dup
1307             Usage : $db->get_dup($key, $want_hash)
1308             Function: retrieve all records associated with a key
1309             Returns : scalar context: scalar number of records
1310             array context, !$want_hash: array of values
1311             array context, $want_hash: hash of value-count pairs
1312             Args : as in DB_File
1313              
1314             =cut
1315              
1316             sub get_dup {
1317 3     3 1 6 my $self = shift;
1318 3         6 my ($key, $want_hash) = @_;
1319 3 50       7 return unless $self->dbh;
1320 3         7 $self->commit;
1321 3 50       6 unless ($self->dup) {
1322 0         0 carp("DB not created in dup context; ignoring");
1323 0         0 return;
1324             }
1325 3         13 $self->get_sth->execute($key);
1326 3         14 my $ret = $self->get_sth->fetchall_arrayref;
1327 3 100       14 return scalar @$ret unless wantarray;
1328 2         3 my @ret = map {$_->[0]} @$ret;
  6         13  
1329 2 100       5 if (!$want_hash) {
1330 1         7 return @ret;
1331             }
1332             else {
1333 1         1 my %h;
1334 1         6 $h{$_}++ for @ret;
1335 1         8 return %h;
1336             }
1337             }
1338              
1339             =head2 find_dup()
1340              
1341             Title : find_dup
1342             Usage : $db->find_dup($key, $value)
1343             Function: set the cursor to an instance of
1344             the $key-$value pair, if one
1345             exists
1346             Returns : 0 on success, 1 on fail
1347             Args : as in DB_File
1348              
1349             =cut
1350              
1351             sub find_dup {
1352 1     1 1 3 my $self = shift;
1353 1         2 my ($key, $value) = @_;
1354 1 50       3 return unless $self->dbh;
1355 1         3 $self->commit;
1356 1 50       2 unless ($self->dup) {
1357 0         0 carp("DB not created in dup context; ignoring");
1358 0         0 return;
1359             }
1360 1         11 $self->sel_dup_sth->bind_param(1,$key);
1361 1         6 $self->sel_dup_sth->bind_param(2,$value,SQL_BLOB);
1362 1         5 $self->sel_dup_sth->execute;
1363 1         6 my $ret = $self->sel_dup_sth->fetch;
1364 1 50       9 return 1 unless $ret;
1365 1         3 ${$self->CURSOR} = _find_idx($ret->[0], $self->SEQIDX);
  1         7  
1366 1         6 return 0
1367             }
1368              
1369             =head2 del_dup()
1370              
1371             Title : del_dup
1372             Usage : $db->del_dup($key, $value)
1373             Function: delete all instances of the $key-$value pair
1374             Returns : 0 on success, 1 on fail
1375             Args : as in DB_File
1376              
1377             =cut
1378              
1379             sub del_dup {
1380 0     0 1 0 my $self = shift;
1381 0         0 my ($key, $value) = @_;
1382 0         0 my $ret;
1383 0 0       0 return unless $self->dbh;
1384 0 0       0 unless ($self->dup) {
1385 0         0 carp("DB not created in dup context; ignoring");
1386 0         0 return;
1387             }
1388 0         0 $self->sel_dup_sth->bind_param(1, $key);
1389 0         0 $self->sel_dup_sth->bind_param(2, $value, SQL_BLOB);
1390 0         0 $self->sel_dup_sth->execute;
1391 0         0 $ret = $self->sel_dup_sth->fetchall_arrayref;
1392 0 0       0 unless ($ret) {
1393 0         0 return 1;
1394             }
1395 0         0 $self->del_dup_sth->bind_param(1, $key);
1396 0         0 $self->del_dup_sth->bind_param(2, $value, SQL_BLOB);
1397 0 0       0 if ($self->del_dup_sth->execute) {
1398             # update SEQIDX
1399 0         0 foreach (map { $$_[0] } @$ret) {
  0         0  
1400 0         0 delete ${$self->SEQIDX}[_find_idx($_,$self->SEQIDX)];
  0         0  
1401             }
1402 0         0 $self->_wring_SEQIDX;
1403 0         0 $self->{pending} = 1;
1404 0         0 return 0; # success
1405             }
1406             else {
1407 0         0 return 1; # fail
1408             }
1409             }
1410              
1411             # =head2 BDB API Emulation : internals
1412              
1413             # =head2 partial_match()
1414              
1415             # Title : partial_match
1416             # Usage :
1417             # Function: emulate the partial matching of DB_File::seq() with
1418             # R_CURSOR flag
1419             # Returns :
1420             # Args : $key
1421              
1422             # =cut
1423              
1424             sub partial_match {
1425 3     3 0 6 my $self = shift;
1426 3         4 my ($key, $value) = @_;
1427              
1428 3         5 my ($status,$ret, $pk);
1429 3 50       7 unless ($self->ref ne 'ARRAY') {
1430 0         0 croak(__PACKAGE__.": Partial matches not meaningful for arrays");
1431             }
1432 3         7 my $SEQIDX = $self->SEQIDX;
1433 3         7 my $CURSOR = $self->CURSOR;
1434 3         18 $status = !$self->part_seq_sth->execute( $key );
1435 3 50       12 if (!$status) { # success
1436 3 50       22 if ($ret = $self->{part_seq_sth}->fetch) {
1437 3         15 $_[0] = $ret->[0]; $_[1] = $ret->[1];
  3         5  
1438 3         5 $pk = $ret->[2];
1439 3 50       6 unless (defined($$CURSOR = _find_idx($pk,$SEQIDX))) {
1440 0         0 croak(__PACKAGE__.": Primary key value disappeared! Please submit bug report!");
1441             }
1442 3         8 return 0;
1443             }
1444             }
1445 0         0 return 1;
1446             }
1447              
1448             =head1 SQL Interface
1449              
1450             =head2 dbh()
1451              
1452             Title : dbh
1453             Usage : $db->dbh()
1454             Function: Get/set DBI database handle
1455             Example :
1456             Returns : DBI database handle
1457             Args :
1458              
1459             =cut
1460              
1461             sub dbh {
1462 728     728 1 916 my $self = shift;
1463 728 100       1291 return $self->{'dbh'} = shift if @_;
1464 714         297155 return $self->{'dbh'};
1465             }
1466              
1467             =head2 sth()
1468              
1469             Title : sth
1470             Usage : $obj->sth($stmt_descriptor)
1471             Function: DBI statement handle generator
1472             Returns : a prepared DBI statement handle
1473             Args : scalar string (statement descriptor)
1474             Note : Calls such as $db->put_sth are autoloaded through
1475             this method; please see source for valid descriptors
1476              
1477             =cut
1478              
1479             sub sth {
1480 349     349 1 493 my $self = shift;
1481 349         429 my $desc = shift;
1482 349 50       520 croak(__PACKAGE__.": No active database handle") unless $self->dbh;
1483 349         574 my $tbl = $STMT{$self->ref};
1484 349 50       588 unless ($tbl) {
1485 0         0 croak(__PACKAGE__.": Tied type '".$self->ref."' not recognized");
1486             }
1487 349 100       735 if (!$self->{"${desc}_sth"}) {
1488 40 50       626 croak(__PACKAGE__.": Statement descriptor '$desc' not recognized for type ".$self->ref) unless grep(/^$desc$/,keys %$tbl);
1489 40         120 $self->{"${desc}_sth"} = $self->dbh->prepare($tbl->{$desc});
1490             }
1491 349         5729 return $self->{"${desc}_sth"};
1492             }
1493              
1494             # autoload statement handle getters
1495             # autoload filters
1496              
1497             sub AUTOLOAD {
1498 349     349   490 my $self = shift;
1499 349         824 my @pth = split(/::/, $AUTOLOAD);
1500 349         499 my $desc = $pth[-1];
1501 349 50       1488 unless ($desc =~ /^(.*?)_sth$/) {
1502 0         0 croak(__PACKAGE__.": Subroutine '$AUTOLOAD' is undefined in ".__PACKAGE__);
1503             }
1504 349         699 $desc = $1;
1505 349 50       598 if (defined $desc) {
1506 349 50       383 unless (grep /^$desc$/, keys %{$STMT{$self->ref}}) {
  349         565  
1507 0         0 croak(__PACKAGE__.": Statement accessor ${desc}_sth not defined for type ".$self->ref);
1508             }
1509 349         867 return $self->sth($desc);
1510             }
1511             else {
1512 0         0 croak __PACKAGE__.": Shouldn't be here; call was to '$pth[-1]'";
1513             }
1514             }
1515              
1516             =head2 commit()
1517              
1518             Title : commit
1519             Usage : $db->commit()
1520             Function: commit transactions
1521             Returns :
1522             Args : commit(1) forces, commit() commits when
1523             number of pending transactions > $SQLite::MAXPEND
1524              
1525             =cut
1526              
1527             sub commit {
1528              
1529 149     149 1 188 my $self = shift;
1530              
1531 149 100 66     553 if (@_ or ($self->{pending} > $SQLite_File::MAXPEND)) {
1532 14 50       51 carp(__PACKAGE__.": commit failed") unless $self->dbh->commit();
1533 14         88 $self->{pending} = 0;
1534             }
1535 149         211 return 1;
1536             }
1537              
1538             =head2 pending()
1539              
1540             Title : pending
1541             Usage : $db->pending
1542             Function: Get count of pending (uncommitted) transactions
1543             Returns : scalar int
1544             Args : none
1545              
1546             =cut
1547              
1548             sub pending {
1549 0     0 1 0 shift->{pending};
1550             }
1551              
1552             =head2 trace()
1553              
1554             Title : trace
1555             Usage : $db->trace($TraceLevel)
1556             Function: invoke the DBI trace logging service
1557             Returns : the trace level
1558             Args : scalar int trace level
1559              
1560             =cut
1561              
1562             sub trace {
1563 0     0 1 0 my $self = shift;
1564 0         0 my $level = shift;
1565 0 0       0 return unless $self->dbh;
1566 0   0     0 $level ||= 3;
1567 0         0 $self->dbh->{TraceLevel} = $level;
1568 0         0 $self->dbh->trace;
1569 0         0 return $level;
1570             }
1571              
1572             # =head1 Private index methods : Internal
1573              
1574             # =head2 _index_is_stale()
1575              
1576             # Title : _index_is_stale
1577             # Usage :
1578             # Function: predicate indicating whether a _reindex has been
1579             # performed since adding or updating the db
1580             # Returns :
1581             # Args : none
1582              
1583             # =cut
1584              
1585             sub _index_is_stale {
1586 22     22   29 my $self = shift;
1587 22         68 return $self->{_stale};
1588             }
1589              
1590             # =head2 _index()
1591              
1592             # Title : _index
1593             # Usage :
1594             # Function: initial the internal index array (maps sequential
1595             # coordinates to db primary key integers)
1596             # Returns : 1 on success
1597             # Args : none
1598              
1599             # =cut
1600              
1601             sub _index {
1602 0     0   0 my $self = shift;
1603              
1604 0 0       0 croak(__PACKAGE__.": _index not meaningful for index type '".$self->index->{type}."'") unless $self->index->{type} eq 'BINARY';
1605 0         0 my ($q, @order);
1606 0         0 $q = $self->dbh->selectall_arrayref("SELECT pk, id FROM hash ORDER BY id");
1607 0 0       0 unless ($q) {
1608 0         0 return 0;
1609             }
1610 0         0 @order = map { $$_[0] } @$q;
  0         0  
1611 0         0 $self->{SEQIDX} = \@order;
1612 0         0 ${$self->CURSOR} = 0;
  0         0  
1613 0         0 $self->{_stale} = 0;
1614 0         0 return 1;
1615             }
1616              
1617             # =head2 _reindex()
1618              
1619             # Title : _reindex
1620             # Usage :
1621             # Function: reorder SEQIDX to reflect BTREE ordering,
1622             # preserving cursor
1623             # Returns : true on success
1624             # Args : none
1625              
1626             # =cut
1627              
1628             sub _reindex {
1629 4     4   6 my $self = shift;
1630              
1631 4 50       7 croak(__PACKAGE__.": _reindex not meaningful for index type '".$self->index->{type}."'") unless $self->index->{type} eq 'BINARY';
1632 4         7 my ($q, @order);
1633 4         7 my $SEQIDX = $self->SEQIDX;
1634 4         11 my $CURSOR = $self->CURSOR;
1635 4         12 $self->_wring_SEQIDX;
1636 4         13 $q = $self->dbh->selectall_arrayref("SELECT pk, id FROM hash ORDER BY id");
1637 4 50       369 unless ($q) {
1638 0         0 return 0;
1639             }
1640 4         14 @order = map { $$_[0] } @$q;
  25         36  
1641 4 50       12 if (defined $$CURSOR) {
1642 4         13 $$CURSOR = _find_idx($$SEQIDX[$$CURSOR],\@order);
1643             }
1644             else {
1645 0         0 $$CURSOR = 0;
1646             }
1647 4         9 $self->{SEQIDX} = \@order;
1648 4         7 $self->{_stale} = 0;
1649 4         10 return 1;
1650             }
1651              
1652             # =head2 _find_idx()
1653              
1654             # Title : _find_idx
1655             # Usage :
1656             # Function: search of array for index corresponding
1657             # to a given value
1658             # Returns : scalar int (target array index)
1659             # Args : scalar int (target value), array ref (index array)
1660              
1661             # =cut
1662              
1663             sub _find_idx {
1664 10     10   20 my ($pk, $seqidx) = @_;
1665 10         13 my $i;
1666 10         27 for (0..$#$seqidx) {
1667 39         39 $i = $_;
1668 39 50       61 next unless defined $$seqidx[$_];
1669 39 100       61 last if $pk == $$seqidx[$_];
1670             }
1671 10   33     57 return (defined $$seqidx[$i] and $pk == $$seqidx[$i] ? $i : undef);
1672             }
1673              
1674             # =head2 _wring_SEQIDX()
1675              
1676             # Title : _wring_SEQIDX
1677             # Usage :
1678             # Function: remove undef'ed values from SEQIDX,
1679             # preserving cursor
1680             # Returns :
1681             # Args : none
1682              
1683             # =cut
1684              
1685             sub _wring_SEQIDX {
1686 10     10   13 my $self = shift;
1687 10         18 my $SEQIDX = $self->SEQIDX;
1688 10         19 my $CURSOR = $self->CURSOR;
1689 10 100       21 $$CURSOR = 0 unless defined $$CURSOR;
1690 10         13 my ($i, $j, @a);
1691 10         14 $j = 0;
1692 10         25 for $i (0..$#$SEQIDX) {
1693 66 100       91 if (defined $$SEQIDX[$i]) {
1694 62 100       89 $$CURSOR = $j if $$CURSOR == $i;
1695 62         81 $a[$j++] = $$SEQIDX[$i];
1696             }
1697             else {
1698 4 50       19 $$CURSOR = $i+1 if $$CURSOR == $i;
1699             }
1700             }
1701 10         24 @$SEQIDX = @a;
1702 10         17 return;
1703             }
1704              
1705             # =head2 _get_pk()
1706              
1707             # Title : _get_pk
1708             # Usage :
1709             # Function: provide an unused primary key integer for seq access
1710             # Returns : scalar int
1711             # Args : none
1712              
1713             # =cut
1714              
1715             sub _get_pk {
1716 40     40   57 my $self = shift;
1717             # do the primary key auditing for the cursor functions...
1718 40         70 return ++$AUTOPK;
1719             }
1720              
1721             # =head2 _last_pk
1722              
1723             # Title : _last_pk
1724             # Usage : $obj->_last_pk($newval)
1725             # Function: the primary key integer returned on the last FETCH
1726             # Example :
1727             # Returns : value of _last_pk (a scalar)
1728             # Args : on set, new value (a scalar or undef, optional)
1729              
1730             # =cut
1731              
1732             sub _last_pk {
1733 101     101   135 my $self = shift;
1734            
1735 101 100       259 return $self->{'_last_pk'} = shift if @_;
1736 16         60 return $self->{'_last_pk'};
1737             }
1738              
1739             # =head2 Array object helper functions : internal
1740              
1741             # =cut
1742              
1743             sub len {
1744 38     38 0 44 scalar @{shift->SEQIDX};
  38         59  
1745             }
1746              
1747             sub get_idx {
1748 55     55 0 72 my $self = shift;
1749 55         60 my $index = shift;
1750 55         88 my $SEQIDX = $self->SEQIDX;
1751 55 100       528 return $$SEQIDX[$index] if defined $$SEQIDX[$index];
1752 16         25 push @$SEQIDX, $AUTOKEY;
1753 16         540 $$SEQIDX[$index] = $AUTOKEY++;
1754             }
1755              
1756             sub shift_idx {
1757 3     3 0 5 my $self = shift;
1758 3         4 return shift( @{$self->SEQIDX} );
  3         4  
1759             }
1760              
1761             # returns the set of new db ids to use
1762             sub unshift_idx {
1763 2     2 0 4 my $self = shift;
1764 2         3 my $n = shift;
1765 2         3 my @new;
1766 2         48 push(@new, $AUTOKEY++) for (0..$n-1);
1767 2         4 unshift @{$self->SEQIDX}, @new;
  2         5  
1768 2         6 return @new;
1769             }
1770              
1771             sub rm_idx {
1772 4     4 0 9 my $self = shift;
1773 4         5 my $index = shift;
1774 4 50       5 unless (delete ${$self->SEQIDX}[$index]) {
  4         7  
1775 0         0 warn("Element $index did not exist");
1776             }
1777             }
1778              
1779             1;
1780              
1781              
1782             package #hide from PAUSE
1783             SQLite_File::HASHINFO;
1784 6     6   59 use strict;
  6         10  
  6         146  
1785 6     6   27 use warnings;
  6         9  
  6         553  
1786              
1787             # a hashinfo class stub
1788             sub new {
1789 6     6   12 my $class = shift;
1790 6         14 my $self = bless({}, $class);
1791 6         40 $self->{type} = 'HASH';
1792 6         15 return $self;
1793             }
1794              
1795             1;
1796              
1797             package #hide from PAUSE
1798             SQLite_File::BTREEINFO;
1799 6     6   47 use strict;
  6         11  
  6         127  
1800 6     6   36 use warnings;
  6         10  
  6         481  
1801              
1802             # a btreeinfo class stub
1803             sub new {
1804 6     6   9 my $class = shift;
1805 6         12 my $self = bless({}, $class);
1806 6         22 $self->{type} = 'BINARY';
1807 6         12 return $self;
1808             }
1809              
1810             1;
1811              
1812             package #hide from PAUSE
1813             SQLite_File::RECNOINFO;
1814 6     6   39 use strict;
  6         20  
  6         143  
1815 6     6   28 use warnings;
  6         8  
  6         454  
1816              
1817             # a recnoinfo class stub
1818             sub new {
1819 6     6   9 my $class = shift;
1820 6         11 my $self = bless({}, $class);
1821 6         19 $self->{type} = 'RECNO';
1822 6         11 return $self;
1823             }
1824              
1825             1;