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 AnyDBM_File::Importer 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   675178 use base qw/Tie::Hash Tie::Array Exporter/;
  6         62  
  6         3209  
156 6     6   13369 use strict;
  6         15  
  6         141  
157 6     6   35 use warnings;
  6         9  
  6         255  
158             our $VERSION = '0.1006';
159              
160 6     6   34 use vars qw( $AUTOLOAD ) ;
  6         10  
  6         350  
161              
162             BEGIN {
163 6 50   6   359 unless (eval "require DBD::SQLite; 1") {
164 0         0 croak( "SQLite_File requires DBD::SQLite" );
165             }
166             }
167              
168 6     6   45 use Fcntl qw(O_CREAT O_RDWR O_RDONLY O_TRUNC);
  6         12  
  6         383  
169 6     6   35 use DBI qw(:sql_types);
  6         13  
  6         2778  
170 6     6   3727 use File::Temp qw( tempfile );
  6         103933  
  6         402  
171 6     6   51 use Carp;
  6         12  
  6         52664  
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 1051 sub R_DUP { 32678 }
188 74     74 0 220 sub R_CURSOR { 27 }
189 34     34 0 122 sub R_FIRST { 7 }
190 26     26 0 66 sub R_LAST { 15 }
191 32     32 0 82 sub R_NEXT { 16 }
192 8     8 0 26 sub R_PREV { 23 }
193 4     4 0 12 sub R_IAFTER { 1 }
194 3     3 0 10 sub R_IBEFORE { 3 }
195 2     2 0 7 sub R_NOOVERWRITE { 20 }
196 7     7 0 23 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 350 my $self = shift;
236 249 100       586 return $self->{SEQIDX} = [] if (!defined $self->{SEQIDX});
237 238         584 return $self->{SEQIDX};
238             }
239              
240             sub CURSOR {
241 62     62 0 81 my $self = shift;
242 62         114 return \$self->{CURSOR};
243             }
244              
245             sub TIEHASH {
246 11     11   97012 my $class = shift;
247 11         67 my ($file, $flags, $mode, $index, $keep) = @_;
248 11   100     72 $flags //= O_CREAT|O_RDWR;
249 11         40 my $self = {};
250 11         39 bless($self, $class);
251             # allow $mode to be skipped
252 11 50       51 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     55 $mode ||= 0644;
258 11   66     144 $index ||= $DB_HASH;
259 11 50 33     189 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         119 $self->{ref} = 'HASH';
264 11         46 $self->{index} = $index;
265 11         38 $self->{pending} = 0;
266 11         35 my ($infix,$fh);
267             # db file handling
268 11 100       41 if ($file) {
269             # you'll love this...
270 8         22 my $setmode;
271 8         29 for ($flags) {
272 8 100       45 $_ eq 'O_SVWST' && do { #bullsith kludge
273 1         7 $_ = 514;
274             };
275 8 100       45 ($_ & O_CREAT) && do {
276 6 100       136 $setmode = 1 if ! -e $file;
277 6 100       66 $infix = (-e $file ? '<' : '>');
278             };
279 8 50       43 ($_ & O_RDWR) && do {
280 8 100       32 $infix = '+'.($infix ? $infix : '<');
281             };
282 8 100       38 ($_ & O_TRUNC) && do {
283 1         8 $infix = '>';
284             };
285 8         13 do { # O_RDONLY
286 8 50       28 $infix = '<' unless $infix;
287             };
288             }
289 8 50       523 open($fh, $infix, $file) or croak(__PACKAGE__.": Can't open db file: $!");
290 8 100       126 chmod $mode, $file if $setmode;
291             # if file explicitly specified, but keep is not,
292             # retain file at destroy...
293 8 100       49 $keep = 1 if !defined $keep;
294             }
295             else {
296             # if no file specified, use a temp file...
297 3         19 ($fh, $file) = tempfile(EXLOCK => 0);
298             # if keep not explicitly specified,
299             # remove the tempfile at destroy...
300 3 50       1550 $keep = 0 if !defined $keep;
301             }
302 11         86 $self->file($file);
303 11         46 $self->_fh($fh);
304 11         44 $self->keep($keep);
305              
306             # create SQL statements
307             my $hash_tbl = sub {
308 11     11   34 my $col = shift;
309 11   100     114 $col ||= 'nocase';
310 11         103 return <
311             (
312             id blob collate $col,
313             obj blob not null,
314             pk integer primary key autoincrement
315             );
316             END
317 11         80 };
318 11         39 my $create_idx = <
319             CREATE INDEX IF NOT EXISTS id_idx ON hash ( id, pk );
320             END
321 11         37 my $dbh = DBI->connect("DBI:SQLite:dbname=".$self->file,"","",
322             {RaiseError => 1, AutoCommit => 0});
323 11         13789 $self->dbh( $dbh );
324             # pragmata inspired by Bio::DB::SeqFeature::Store::DBI::SQLite
325             # $dbh->do("PRAGMA synchronous = OFF");
326 11         98 $dbh->do("PRAGMA temp_store = MEMORY");
327 11   50     2765 $dbh->do("PRAGMA cache_size = ".($index->{cachesize} || 20000));
328              
329 11         1081 for ($index->{'type'}) {
330 11   100     75 my $flags = $index->{flags} || 0;
331 11 50       63 !defined && do {
332 0         0 $self->dbh->do("CREATE TABLE IF NOT EXISTS hash $hash_tbl");
333 0         0 last;
334             };
335 11 100       50 $_ eq 'BINARY' && do {
336 4         9 my $col = 'nocase';
337 4 100       17 if (ref($index->{'compare'}) eq 'CODE') {
338 2         10 $self->dbh->func( 'usr', $index->{'compare'}, "create_collation");
339 2         34 $col = 'usr';
340             }
341 4 100       17 if ($flags & R_DUP ) {
342 3         15 $self->dup(1);
343 3         12 $self->dbh->do("CREATE TABLE IF NOT EXISTS hash ".$hash_tbl->($col));
344 3         941 $self->dbh->do($create_idx);
345             }
346             else {
347 1         9 $self->dup(0);
348 1         4 $self->dbh->do("CREATE TABLE IF NOT EXISTS hash ".$hash_tbl->($col));
349 1         340 $self->dbh->do($create_idx);
350             }
351 4         707 last;
352             };
353 7 50       30 $_ eq 'HASH' && do {
354 7         28 $self->dbh->do("CREATE TABLE IF NOT EXISTS hash ".$hash_tbl->());
355 7         1769 $self->dbh->do($create_idx);
356 7         1025 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     119 $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         64 my ($sth)=$self->dbh->prepare("select max(pk) from hash");
370 11         1896 $sth->execute();
371 11         149 ($AUTOPK)=$sth->fetchrow_array();
372 11         347 return $self;
373             }
374              
375             sub TIEARRAY {
376 3     3   4475 my $class = shift;
377 3         19 my ($file, $flags, $mode, $index, $keep) = @_;
378 3   100     18 $flags //= O_CREAT|O_RDWR;
379 3         10 my $self = {};
380 3         11 bless($self, $class);
381              
382 3         20 $self->{ref} = 'ARRAY';
383             # allow $mode to be skipped
384 3 50       17 if (ref($mode) =~ /INFO$/) { # it's the index type
385 0         0 $index = $mode;
386 0         0 $mode = 0644;
387             }
388 3   100     22 $mode ||= 0644;
389 3   66     30 $index ||= $DB_RECNO;
390 3 50 33     67 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       31 $index->{type} eq 'RECNO';
395 3         8 $self->{index} = $index;
396 3         9 $self->{pending} = 0;
397 3         7 my ($infix,$fh);
398             # db file handling
399 3 100       12 if ($file) {
400 1         1 my $setmode;
401 1         4 for ($flags) {
402 1 50       4 $_ eq 'O_SVWST' && do { #bullsith kludge
403 0         0 $_ = 514;
404             };
405 1 50       4 ($_ & O_CREAT) && do {
406 1 50       16 $setmode = 1 if ! -e $file;
407 1 50       14 $infix = (-e $file ? '<' : '>');
408             };
409 1 50       4 ($_ & O_RDWR) && do {
410 1 50       4 $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       4 $infix = '<' unless $infix;
417             };
418             }
419 1 50       41 open($fh, $infix, $file) or croak(__PACKAGE__.": Can't open db file: $!");
420 1 50       5 chmod $mode, $file if $setmode;
421             # if file explicitly specified, but keep is not,
422             # retain file at destroy...
423 1 50       5 $keep = 1 if !defined $keep;
424             }
425             else {
426             # if no file specified, use a temp file...
427 2         14 ($fh, $file) = tempfile(EXLOCK => 0);
428             # if keep not explicitly specified,
429             # remove the tempfile at destroy...
430 2 50       1085 $keep = 0 if !defined $keep;
431             }
432 3         19 $self->file($file);
433 3         11 $self->_fh($fh);
434 3         12 $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         8 my $create_idx = <
444             CREATE INDEX IF NOT EXISTS id_idx ON hash ( id );
445             END
446            
447 3         11 my $dbh = DBI->connect("dbi:SQLite:dbname=".$self->file,"","",
448             {RaiseError => 1, AutoCommit => 0});
449 3         1232 $self->dbh( $dbh );
450              
451 3         11 for ($index->{'type'}) {
452 3   50     24 my $flags = $index->{flags} || 0;
453 3 50       16 $_ 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       12 $_ 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       12 $_ eq 'RECNO' && do {
464 3         10 $self->dbh->do("CREATE TABLE IF NOT EXISTS hash $arr_tbl");
465 3         1654 $self->dbh->do($create_idx);
466 3         469 my $r = $self->dbh->selectall_arrayref("select * from hash");
467 3         241 for (@$r) {
468 3         6 push @{$self->SEQIDX},$$_[0];
  3         7  
469             }
470 3         12 last;
471             };
472 0         0 do {
473 0         0 croak(__PACKAGE__.": Index type not defined or not recognized");
474             };
475             }
476 3         15 $self->commit(1);
477 3         51 return $self;
478             }
479              
480             # common methods for hashes and arrays
481              
482             sub FETCH {
483 60     60   446 my $self = shift;
484 60         94 my $key = shift;
485 60         84 my $fkey;
486 60 50       129 return unless $self->dbh;
487 60         157 $self->commit;
488 60 100 66     275 if (!$self->{ref} or $self->ref eq 'HASH') {
    50          
489 35         74 local $_ = $key;
490 35         98 $self->_store_key_filter;
491 35         221 $self->get_sth->execute($_); # fetches on column 'id'
492             }
493             elsif ($self->ref eq 'ARRAY') {
494 25 100       39 if (defined ${$self->SEQIDX}[$key]) {
  25         51  
495 24         120 $self->get_sth->execute($self->get_idx($key));
496             }
497             else {
498 1         5 $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         390 my $ret = $self->get_sth->fetch;
506 59 100       194 if ($ret) {
507 56         173 $self->_last_pk( $ret->[1] ); # store the returned pk
508 56         122 $ret->[0] =~ s{}{'}g;
509 56         91 $ret->[0] =~ s{}{"}g;
510 56         110 local $_ = $ret->[0];
511 56         172 $self->_fetch_value_filter;
512 56         293 return $_; # always returns the object
513             }
514             else {
515 3         14 $self->_last_pk( undef ); # fail in pk
516 3         10 return $ret;
517             }
518             }
519              
520             sub STORE {
521 49     49   690 my $self = shift;
522 49         119 my ($key, $value) = @_;
523 49         87 my ($fkey, $fvalue);
524 49 50       125 return unless $self->dbh;
525             {
526             # filter value
527 49         87 local $_ = $value;
528 49         132 $self->_store_value_filter;
529 49         117 $fvalue = $_;
530             }
531             {
532             # filter key
533 49         86 $_ = $key;
  49         63  
  49         76  
534 49         124 $self->_store_key_filter;
535 49         98 $fkey = $_;
536             }
537 49         161 $fvalue =~ s{'}{}g;
538 49         79 $fvalue =~ s{"}{}g;
539 49         74 my ($pk, $sth);
540 49 100 66     194 if ( !defined $self->{ref} or $self->ref eq 'HASH' ) {
    50          
541 36 100       126 if ( $self->dup ) { # allowing duplicates
542 17         42 $pk = $self->_get_pk;
543 17         86 $sth = $self->put_sth;
544 17         97 $sth->bind_param(1,$fkey);
545 17         73 $sth->bind_param(2,$fvalue, SQL_BLOB);
546 17         50 $sth->bind_param(3,$pk);
547 17         61 $self->put_sth->execute();
548 17         140 push @{$self->SEQIDX}, $pk;
  17         46  
549             }
550             else { # no duplicates...
551             #need to check if key is already present
552 19 100       79 if ( $self->EXISTS($key) )
553             {
554 1         9 $sth = $self->upd_sth;
555 1         11 $sth->bind_param(1,$fvalue, SQL_BLOB);
556 1         4 $sth->bind_param(2,$key);
557 1         3 $sth->bind_param(3,$self->_last_pk);
558 1         230 $sth->execute();
559             }
560             else {
561 18         54 $pk = $self->_get_pk;
562 18         97 $sth = $self->put_sth;
563 18         113 $sth->bind_param(1,$fkey);
564 18         106 $sth->bind_param(2,$fvalue, SQL_BLOB);
565 18         53 $sth->bind_param(3,$pk);
566 18         1220 $sth->execute();
567 18         50 push @{$self->SEQIDX}, $pk;
  18         61  
568             }
569             }
570 36         91 $self->{_stale} = 1;
571             }
572             elsif ( $self->ref eq 'ARRAY' ) {
573             # need to check if the key is already present
574 13 100       19 if (!defined ${$self->SEQIDX}[$key] ) {
  13         25  
575 12         64 $self->put_sth->execute($self->get_idx($key), $fvalue);
576             }
577             else {
578 1         20 $self->upd_sth->execute($fvalue,$self->get_idx($key));
579             }
580             }
581 49         109 ++$self->{pending};
582 49         251 $value;
583             }
584              
585             sub DELETE {
586 3     3   13 my $self = shift;
587 3         7 my $key = shift;
588 3 50       10 return unless $self->dbh;
589 3         12 my $fkey;
590             { # filter key
591 3         7 local $_ = $key;
  3         9  
592 3         11 $self->_store_key_filter;
593 3         9 $fkey = $_;
594             }
595 3 50 66     13 $self->_reindex if ($self->index->{type} eq 'BINARY' and $self->_index_is_stale);
596 3         7 my $oldval;
597 3 100 66     9 if (!$self->ref or $self->ref eq 'HASH') {
    50          
598 2 50       13 return unless $self->get_sth->execute($fkey);
599 2         15 my $ret = $self->get_sth->fetch;
600 2         7 $oldval = $ret->[0];
601 2         23 $self->del_sth->execute($fkey); # del on id
602             # update the sequential side
603 2 50       12 if ($ret->[1]) {
604 2         4 delete ${$self->SEQIDX}[_find_idx($ret->[1],$self->SEQIDX)];
  2         7  
605             }
606             }
607             elsif ($self->ref eq 'ARRAY') {
608 1         4 my $SEQIDX = $self->SEQIDX;
609 1 50       4 if ($$SEQIDX[$key]) {
610 1         4 $oldval = $self->FETCH($$SEQIDX[$key]);
611             # $self->dbh->do("DELETE FROM hash WHERE id = '$$SEQIDX[$key]'");
612 1         6 $self->del_sth->execute($$SEQIDX[$key]); # del on id
613 1         10 $self->rm_idx($key);
614             }
615             }
616             else {
617 0         0 croak( __PACKAGE__.": tied type not recognized" );
618             }
619 3         9 ++$self->{pending};
620 3         7 $_ = $oldval;
621 3         13 $self->_fetch_value_filter;
622 3         22 return $_;
623             }
624              
625             sub EXISTS {
626 25     25   91 my $self = shift;
627 25         40 my $key = shift;
628 25 50       54 return unless $self->dbh;
629              
630 25         73 $self->commit;
631 25 50 33     77 if (!$self->ref or $self->ref eq 'HASH') {
    0          
632 25         60 local $_ = $key;
633 25         56 $self->_store_key_filter;
634 25         181 $self->get_sth->execute($_);
635 25         172 my $ret = $self->get_sth->fetch;
636 25 100       116 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   6 my $self = shift;
648 2 50       9 return unless $self->dbh;
649 2         9 $self->dbh->commit;
650 2         6 my $sth = $self->dbh->prepare("DELETE FROM hash");
651 2         537 $sth->execute;
652 2         14 $self->dbh->commit;
653 2         15 @{$self->SEQIDX} = ();
  2         26  
654 2         59 return 1;
655             }
656              
657             # hash methods
658              
659             sub FIRSTKEY {
660 6     6   261 my $self = shift;
661 6 50       17 return unless $self->dbh;
662 6         20 $self->commit;
663 6 50 33     25 return if ($self->{ref} and $self->ref ne 'HASH');
664 6         17 my $ids = $self->dbh->selectall_arrayref("SELECT id FROM hash");
665 6 50       590 return unless $ids;
666 6         21 $ids = [ map { $_->[0] } @$ids ];
  27         68  
667             { # filter keys
668 6         13 $self->_fetch_key_filter for (@$ids);
  6         55  
669             }
670 6         24 return $self->_keys($ids);
671             }
672              
673             sub NEXTKEY {
674 27     27   55 my $self = shift;
675 27         37 my $lastkey = shift;
676 27 50       46 return unless $self->dbh;
677 27 50 33     88 return if ($self->{ref} and $self->ref ne 'HASH');
678 27         52 return $self->_keys;
679             }
680              
681             # array methods
682              
683             sub FETCHSIZE {
684 25     25   976 my $self = shift;
685 25 50       55 return unless $self->dbh;
686 25 50 33     76 return if (!$self->{ref} or $self->ref ne 'ARRAY');
687 25         59 $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   8 my $self = shift;
710 2         6 my $count = shift;
711 2         13 return;
712             }
713              
714             sub POP {
715 3     3   9 my $self = shift;
716 3 50       10 return unless $self->dbh;
717 3         10 $self->commit;
718 3 50 33     12 return if (!$self->{ref} or $self->ref ne 'ARRAY');
719 3         17 $self->get_sth->execute($self->get_idx($self->len-1));
720 3         16 my $ret = $self->get_sth->fetch;
721             # $self->dbh->do("DELETE FROM hash WHERE id = ".$self->get_idx($self->len-1));
722 3         14 $self->del_sth->execute($self->get_idx($self->len-1));
723             # bookkeeping
724 3         12 $self->rm_idx($self->len-1);
725 3 50       17 return defined $ret ? $ret->[0] : $ret;
726             }
727              
728             sub PUSH {
729 2     2   5 my $self = shift;
730 2         7 my @values = @_;
731 2 50       7 return unless $self->dbh;
732 2 50 33     13 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         22 $self->put_sth->execute($self->get_idx($i), shift @values);
738             }
739 2         6 ++$self->{pending};
740 2         9 return $ret;
741             }
742              
743             sub SHIFT {
744 3     3   9 my $self = shift;
745 3 50       8 return unless $self->dbh;
746 3         10 $self->commit;
747 3 50 33     13 return if (!$self->{ref} or $self->ref ne 'ARRAY');
748 3         15 $self->get_sth->execute( $self->get_idx(0) );
749 3         17 my $ret = $self->get_sth->fetch;
750 3         15 $self->del_sth->execute($self->get_idx(0));
751             # bookkeeping
752 3         27 $self->shift_idx;
753 3   33     17 $_ = $ret && $ret->[0];
754 3         10 $self->_fetch_value_filter;
755 3         11 return $_;
756             }
757              
758             sub UNSHIFT {
759 2     2   6 my $self = shift;
760 2         7 my @values = @_;
761 2 50 33     15 return if (!$self->{ref} or $self->ref ne 'ARRAY');
762 2         6 my $n = @values;
763 2         8 $self->_store_value_filter for @values;
764 2 50       8 return unless $self->dbh;
765 2         12 for ($self->unshift_idx($n)) {
766 3         16 $self->put_sth->execute($_,shift @values);
767             }
768 2         8 ++$self->{pending};
769 2         9 return $n;
770             }
771              
772             sub SPLICE {
773 1     1   555 my $self = shift;
774 1   50     5 my $offset = shift || 0;
775 1   33     5 my $length = shift || $self->FETCHSIZE() - $offset;
776 1         4 my @list = @_;
777 1         5 my $SEQIDX = $self->SEQIDX;
778 1         6 $self->_wring_SEQIDX;
779 1         5 my @pk = map { $self->get_idx($_)} ($offset..$offset+$length-1);
  2         6  
780 1         3 my @ret;
781 1         3 for (@pk) {
782 2         12 $self->get_sth->execute($_);
783 2         6 push @ret, ${$self->get_sth->fetch}[0];
  2         10  
784 2         10 $self->del_sth->execute($_);
785             }
786 1         5 my @new_idx = map { $AUTOKEY++ } @list;
  3         10  
787 1         8 splice( @$SEQIDX, $offset, $length, @new_idx );
788 1         8 $self->put_sth->execute($_, shift @list) for @new_idx;
789 1         11 $self->_fetch_value_filter for @ret;
790 1         10 return @ret;
791             }
792              
793             # destructors
794              
795             sub UNTIE {
796 8     8   694 my $self = shift;
797 8         21 my $count = shift;
798              
799 8 50       47 croak( __PACKAGE__.": untie attempted while $count inner references still exist" ) if ($count);}
800              
801             sub DESTROY {
802 14     14   1961 my $self = shift;
803 14         48 $self->dbh->commit; #'hard' commit
804 14         202 my $tbl = $STMT{$self->ref};
805             # finish and destroy stmt handles
806 14         116 for ( keys %$tbl ) {
807 145 100       609 $self->{$_."_sth"}->finish if $self->{$_."_sth"};
808 145         817 undef $self->{$_."_sth"};
809             }
810             # disconnect
811 14 50       77 croak($self->dbh->errstr) unless $self->dbh->disconnect;
812 14         143 $self->{dbh}->DESTROY;
813 14         642 undef $self->{dbh};
814             # remove file if nec
815 14 50       76 $self->_fh->close() if $self->_fh;
816 14 100       418 if (-e $self->file) {
817 13         155 local $!;
818 13 100 66     53 unlink $self->file if (!$self->keep && $self->_fh);
819 13 50       119 $! && carp(__PACKAGE__.": unlink issue: $!");
820             }
821 14         35 undef $self;
822 14         734 1;
823             }
824              
825             # dbm filter storage hooks
826              
827             sub filter_store_key {
828 4     4 0 183 my $self = shift;
829 4         7 my $code = shift;
830            
831 4 50 66     55 unless (!defined($code) or ref($code) eq 'CODE') {
832 0         0 croak(__PACKAGE__."::filter_store_key requires a coderef argument");
833             }
834 4         20 $self->_store_key_filter($code);
835            
836             };
837              
838             sub filter_store_value {
839 2     2 0 19 my $self = shift;
840 2         6 my $code = shift;
841 2 50 33     16 unless (!defined($code) or ref($code) eq 'CODE') {
842 0         0 croak(__PACKAGE__."::filter_store_value requires a coderef argument");
843             }
844 2         9 $self->_store_value_filter($code);
845            
846             };
847              
848             sub filter_fetch_key {
849 3     3 0 659 my $self = shift;
850 3         7 my $code = shift;
851 3 100 66     22 unless (!defined($code) or ref($code) eq 'CODE') {
852 1         21 croak(__PACKAGE__."::filter_fetch_key requires a coderef argument");
853             }
854 2         9 $self->_fetch_key_filter($code);
855             };
856              
857             sub filter_fetch_value {
858 2     2 0 10 my $self = shift;
859 2         5 my $code = shift;
860 2 50 33     16 unless (!defined($code) or ref($code) eq 'CODE') {
861 0         0 croak(__PACKAGE__."::filter_fetch_value requires a coderef argument");
862             }
863 2         9 $self->_fetch_value_filter($code);
864             };
865              
866             # filters
867              
868             sub _fetch_key_filter {
869 29     29   57 my $self = shift;
870 29 100       60 if (@_) {
871 2         11 $self->{_fetch_key_filter} = shift;
872 2         8 return 1;
873             }
874 27 100       65 return unless defined $self->{_fetch_key_filter};
875 6         12 &{$self->{_fetch_key_filter}};
  6         10  
876             };
877              
878             sub _fetch_value_filter {
879 66     66   98 my $self = shift;
880 66 100       148 if (@_) {
881 2         6 $self->{_fetch_value_filter} = shift;
882 2         5 return 1;
883             }
884 64 100       209 return unless defined $self->{_fetch_value_filter};
885 7         18 &{$self->{_fetch_value_filter}};
  7         18  
886             };
887              
888             sub _store_key_filter {
889 116     116   207 my $self = shift;
890 116 100       232 if (@_) {
891 4         16 $self->{_store_key_filter} = shift;
892 4         15 return 1;
893             }
894 112 100       237 return unless defined $self->{_store_key_filter};
895 15         26 &{$self->{_store_key_filter}};
  15         36  
896             };
897              
898             sub _store_value_filter {
899 54     54   78 my $self = shift;
900 54 100       114 if (@_) {
901 2         5 $self->{_store_value_filter} = shift;
902 2         55 return 1;
903             }
904 52 100       123 return unless defined $self->{_store_value_filter};
905 7         9 &{$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 96 my $self = shift;
923            
924 50 100       177 return $self->{'file'} = shift if @_;
925 36         917 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   94 my $self = shift;
941            
942 49 100       177 return $self->{'_fh'} = shift if @_;
943 35         326 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 53 my $self = shift;
958            
959 28 100       84 return $self->{'keep'} = shift if @_;
960 13         73 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 1408 my $self = shift;
976 1031         7715 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 80 my $self = shift;
992 54         214 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   46 my $self = shift;
1008 33         45 my $load = shift;
1009 33 100       61 if ($load) {
1010 6         18 $self->{'_keys'} = {};
1011 6         20 @{$self->{'_keys'}}{ @$load } = (undef) x @$load;
  6         31  
1012 6         10 my $a = keys %{$self->{'_keys'}}; #reset each
  6         26  
1013             }
1014 33         43 return each %{$self->{'_keys'}};
  33         195  
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         8 my ($key, $value) = @_;
1033 2 50       5 return unless $self->dbh;
1034 2 100       7 $_[1] = ($self->ref eq 'ARRAY' ? $self->FETCH(${$self->SEQIDX}[$key]) : $self->FETCH($key));
  1         3  
1035 2 50       15 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 26 my $self = shift;
1054 8         19 my ($key, $value, $flags) = @_;
1055 8 50       20 return unless $self->dbh;
1056              
1057 8         18 my $SEQIDX = $self->SEQIDX;
1058 8         19 my $CURSOR = $self->CURSOR;
1059 8         22 my ($status, $pk, @parms);
1060 8         0 my ($sth, $do_cursor);
1061 8         15 for ($flags) {
1062 8 100 100     30 (!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         17 $self->FETCH($key);
1074 5   66     12 $pk = $self->_last_pk || $self->_get_pk;
1075 5 100       12 $sth = ($self->_last_pk ?
1076             $self->upd_seq_sth :
1077             $self->put_seq_sth);
1078             $do_cursor = sub {
1079 5 100   5   15 push @$SEQIDX, $pk if !$self->_last_pk;
1080 5 100       16 $flags && do { # R_SETCURSOR
1081 2 50       7 if ( $pk = $$SEQIDX[-1] ) {
1082 2         5 $$CURSOR = $#$SEQIDX;
1083             }
1084             else {
1085 0         0 $$CURSOR = _find_idx($pk, $SEQIDX);
1086             };
1087 2 100       5 $self->_reindex if $self->index->{type} eq 'BINARY';
1088             };
1089 5         37 };
1090             }
1091 5         12 last;
1092             };
1093 3 100       9 $_ == R_IAFTER && do {
1094 1 50       4 $self->_wring_SEQIDX unless $$SEQIDX[$$CURSOR];
1095             # duplicate protect
1096 1 0 33     4 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       3 $self->index->{type} eq 'RECNO';
1099 1         3 $pk = $self->_get_pk;
1100 1         7 $sth = $self->put_seq_sth;
1101 1         4 $_[0] = $$CURSOR+1;
1102             $do_cursor = sub {
1103 1 50   1   5 if ($$CURSOR == $#$SEQIDX) {
1104 1         4 push @$SEQIDX, $pk;
1105             }
1106             else {
1107 0         0 splice(@$SEQIDX,$$CURSOR,0,$pk);
1108             }
1109 1         5 };
1110 1         2 last;
1111             };
1112 2 100       6 $_ == 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       4 $self->index->{type} eq 'RECNO';
1118 1         3 $pk = $self->_get_pk;
1119 1         7 $sth = $self->put_seq_sth;
1120 1         4 $_[0] = $$CURSOR;
1121             $do_cursor = sub {
1122 1 50   1   5 if ($$CURSOR) {
1123 0         0 splice(@$SEQIDX,$$CURSOR-1,0,$pk);
1124             }
1125             else {
1126 1         4 unshift(@$SEQIDX, $pk);
1127             }
1128 1         3 $$CURSOR++; # preserve cursor
1129 1         8 };
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       4 $_ == R_NOOVERWRITE && do { # put only/add to the "end"
1144             #will create a duplicate if $self->dup is set!
1145 1 50 33     2 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       17 if ($self->ref eq 'ARRAY') {
1156 4         34 $sth->bind_param(1, $value, SQL_BLOB);
1157 4         15 $sth->bind_param(2, $pk);
1158             }
1159             else {
1160 3         16 $sth->bind_param(1, $key);
1161 3         14 $sth->bind_param(2, $value, SQL_BLOB);
1162 3         9 $sth->bind_param(3, $pk);
1163             }
1164 7         208 $status = !$sth->execute;
1165 7 50       34 $do_cursor->() if !$status;
1166 7         16 $self->{pending} = 1;
1167 7 100       20 $self->{_stale} = 0 if $self->index->{type} eq 'BINARY';
1168 7         73 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 6 my $self = shift;
1183 2         5 my ($key, $flags) = @_;
1184 2 50       7 return unless $self->dbh;
1185 2 50 66     6 $self->_reindex if ($self->index->{type} eq 'BINARY' and $self->_index_is_stale);
1186 2         6 my $SEQIDX = $self->SEQIDX;
1187 2         5 my $CURSOR = $self->CURSOR;
1188 2         5 my $status;
1189 2 50       6 if ($flags eq R_CURSOR) {
1190 2 50       7 _wring_SEQIDX($self->SEQIDX) unless $$SEQIDX[$$CURSOR];
1191 2         5 my $pk = $$SEQIDX[$$CURSOR];
1192 2         16 $status = $self->del_seq_sth->execute($pk);
1193 2 50       11 if ($status) { # successful delete
1194 2         6 $$SEQIDX[$$CURSOR] = undef;
1195 2         11 $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         7 $self->{_stale} = 1;
1205 2         4 $self->{pending} = 1;
1206 2 50       14 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 61 my $self = shift;
1228 34         72 my ($key, $value, $flags) = @_;
1229 34 50       78 return 1 unless $flags;
1230 34         89 $self->commit;
1231 34         49 my $status;
1232 34 100 100     69 $self->_reindex if ($self->index->{type} eq 'BINARY' and $self->_index_is_stale);
1233 34         76 my $SEQIDX = $self->SEQIDX;
1234 34         71 my $CURSOR = $self->CURSOR;
1235 34         73 for ($flags) {
1236 34 100       71 $_ eq R_CURSOR && do {
1237 6         12 last;
1238             };
1239 28 100       64 $_ eq R_FIRST && do {
1240 6         12 $$CURSOR = 0;
1241 6         21 last;
1242             };
1243 22 100       48 $_ eq R_LAST && do {
1244 4         11 $$CURSOR = $#$SEQIDX;
1245 4         7 last;
1246             };
1247 18 100       37 $_ eq R_NEXT && do {
1248 14 100       72 return 1 if ($$CURSOR >= $#$SEQIDX);
1249 11         16 ($$CURSOR)++;
1250 11         22 last;
1251             };
1252 4 50       48 $_ eq R_PREV && do {
1253 4 100       16 return 1 if $$CURSOR == 0;
1254 2         4 ($$CURSOR)--;
1255 2         4 last;
1256             };
1257             }
1258 29 100       84 $self->_wring_SEQIDX() unless defined $$SEQIDX[$$CURSOR];
1259             # get by pk, set key and value.
1260 29 100 100     54 if (($flags == R_CURSOR ) && $self->ref eq 'HASH') {
1261 3         18 $status = $self->partial_match($key, $value);
1262 3         7 $_[0] = $key; $_[1] = $value;
  3         34  
1263 3         19 return $status;
1264             }
1265             else {
1266 26         165 $self->get_seq_sth->execute($$SEQIDX[$$CURSOR]);
1267 26         138 my $ret = $self->get_seq_sth->fetch;
1268 26 100       77 ($_[0], $_[1]) = (($self->ref eq 'ARRAY' ? $$CURSOR : $$ret[0]), $$ret[1]);
1269             }
1270 26         121 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 81 my $self = shift;
1300 50 100       115 return $self->{'dup'} = shift if @_;
1301 46         142 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 9 my $self = shift;
1318 3         8 my ($key, $want_hash) = @_;
1319 3 50       7 return unless $self->dbh;
1320 3         9 $self->commit;
1321 3 50       8 unless ($self->dup) {
1322 0         0 carp("DB not created in dup context; ignoring");
1323 0         0 return;
1324             }
1325 3         17 $self->get_sth->execute($key);
1326 3         18 my $ret = $self->get_sth->fetchall_arrayref;
1327 3 100       17 return scalar @$ret unless wantarray;
1328 2         6 my @ret = map {$_->[0]} @$ret;
  6         14  
1329 2 100       7 if (!$want_hash) {
1330 1         8 return @ret;
1331             }
1332             else {
1333 1         3 my %h;
1334 1         6 $h{$_}++ for @ret;
1335 1         11 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         4 my ($key, $value) = @_;
1354 1 50       4 return unless $self->dbh;
1355 1         4 $self->commit;
1356 1 50       4 unless ($self->dup) {
1357 0         0 carp("DB not created in dup context; ignoring");
1358 0         0 return;
1359             }
1360 1         16 $self->sel_dup_sth->bind_param(1,$key);
1361 1         7 $self->sel_dup_sth->bind_param(2,$value,SQL_BLOB);
1362 1         6 $self->sel_dup_sth->execute;
1363 1         7 my $ret = $self->sel_dup_sth->fetch;
1364 1 50       5 return 1 unless $ret;
1365 1         5 ${$self->CURSOR} = _find_idx($ret->[0], $self->SEQIDX);
  1         10  
1366 1         7 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 7 my $self = shift;
1426 3         18 my ($key, $value) = @_;
1427              
1428 3         7 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         8 my $SEQIDX = $self->SEQIDX;
1433 3         16 my $CURSOR = $self->CURSOR;
1434 3         24 $status = !$self->part_seq_sth->execute( $key );
1435 3 50       14 if (!$status) { # success
1436 3 50       39 if ($ret = $self->{part_seq_sth}->fetch) {
1437 3         9 $_[0] = $ret->[0]; $_[1] = $ret->[1];
  3         8  
1438 3         5 $pk = $ret->[2];
1439 3 50       9 unless (defined($$CURSOR = _find_idx($pk,$SEQIDX))) {
1440 0         0 croak(__PACKAGE__.": Primary key value disappeared! Please submit bug report!");
1441             }
1442 3         10 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 1115 my $self = shift;
1463 728 100       1498 return $self->{'dbh'} = shift if @_;
1464 714         279337 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 555 my $self = shift;
1481 349         533 my $desc = shift;
1482 349 50       664 croak(__PACKAGE__.": No active database handle") unless $self->dbh;
1483 349         700 my $tbl = $STMT{$self->ref};
1484 349 50       731 unless ($tbl) {
1485 0         0 croak(__PACKAGE__.": Tied type '".$self->ref."' not recognized");
1486             }
1487 349 100       846 if (!$self->{"${desc}_sth"}) {
1488 40 50       728 croak(__PACKAGE__.": Statement descriptor '$desc' not recognized for type ".$self->ref) unless grep(/^$desc$/,keys %$tbl);
1489 40         156 $self->{"${desc}_sth"} = $self->dbh->prepare($tbl->{$desc});
1490             }
1491 349         6838 return $self->{"${desc}_sth"};
1492             }
1493              
1494             # autoload statement handle getters
1495             # autoload filters
1496              
1497             sub AUTOLOAD {
1498 349     349   568 my $self = shift;
1499 349         965 my @pth = split(/::/, $AUTOLOAD);
1500 349         619 my $desc = $pth[-1];
1501 349 50       1762 unless ($desc =~ /^(.*?)_sth$/) {
1502 0         0 croak(__PACKAGE__.": Subroutine '$AUTOLOAD' is undefined in ".__PACKAGE__);
1503             }
1504 349         821 $desc = $1;
1505 349 50       685 if (defined $desc) {
1506 349 50       455 unless (grep /^$desc$/, keys %{$STMT{$self->ref}}) {
  349         675  
1507 0         0 croak(__PACKAGE__.": Statement accessor ${desc}_sth not defined for type ".$self->ref);
1508             }
1509 349         1010 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 214 my $self = shift;
1530              
1531 149 100 66     679 if (@_ or ($self->{pending} > $SQLite_File::MAXPEND)) {
1532 14 50       57 carp(__PACKAGE__.": commit failed") unless $self->dbh->commit();
1533 14         191 $self->{pending} = 0;
1534             }
1535 149         256 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   36 my $self = shift;
1587 22         84 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   7 my $self = shift;
1630              
1631 4 50       12 croak(__PACKAGE__.": _reindex not meaningful for index type '".$self->index->{type}."'") unless $self->index->{type} eq 'BINARY';
1632 4         10 my ($q, @order);
1633 4         10 my $SEQIDX = $self->SEQIDX;
1634 4         12 my $CURSOR = $self->CURSOR;
1635 4         19 $self->_wring_SEQIDX;
1636 4         14 $q = $self->dbh->selectall_arrayref("SELECT pk, id FROM hash ORDER BY id");
1637 4 50       441 unless ($q) {
1638 0         0 return 0;
1639             }
1640 4         16 @order = map { $$_[0] } @$q;
  25         47  
1641 4 50       21 if (defined $$CURSOR) {
1642 4         23 $$CURSOR = _find_idx($$SEQIDX[$$CURSOR],\@order);
1643             }
1644             else {
1645 0         0 $$CURSOR = 0;
1646             }
1647 4         12 $self->{SEQIDX} = \@order;
1648 4         9 $self->{_stale} = 0;
1649 4         14 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   28 my ($pk, $seqidx) = @_;
1665 10         15 my $i;
1666 10         37 for (0..$#$seqidx) {
1667 39         53 $i = $_;
1668 39 50       71 next unless defined $$seqidx[$_];
1669 39 100       91 last if $pk == $$seqidx[$_];
1670             }
1671 10   33     99 return (defined $i and 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   17 my $self = shift;
1687 10         23 my $SEQIDX = $self->SEQIDX;
1688 10         23 my $CURSOR = $self->CURSOR;
1689 10 100       27 $$CURSOR = 0 unless defined $$CURSOR;
1690 10         19 my ($i, $j, @a);
1691 10         15 $j = 0;
1692 10         33 for $i (0..$#$SEQIDX) {
1693 66 100       107 if (defined $$SEQIDX[$i]) {
1694 62 100       121 $$CURSOR = $j if $$CURSOR == $i;
1695 62         110 $a[$j++] = $$SEQIDX[$i];
1696             }
1697             else {
1698 4 50       12 $$CURSOR = $i+1 if $$CURSOR == $i;
1699             }
1700             }
1701 10         30 @$SEQIDX = @a;
1702 10         21 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   63 my $self = shift;
1717             # do the primary key auditing for the cursor functions...
1718 40         79 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   166 my $self = shift;
1734            
1735 101 100       317 return $self->{'_last_pk'} = shift if @_;
1736 16         76 return $self->{'_last_pk'};
1737             }
1738              
1739             # =head2 Array object helper functions : internal
1740              
1741             # =cut
1742              
1743             sub len {
1744 38     38 0 57 scalar @{shift->SEQIDX};
  38         74  
1745             }
1746              
1747             sub get_idx {
1748 55     55 0 118 my $self = shift;
1749 55         79 my $index = shift;
1750 55         95 my $SEQIDX = $self->SEQIDX;
1751 55 100       660 return $$SEQIDX[$index] if defined $$SEQIDX[$index];
1752 16         35 push @$SEQIDX, $AUTOKEY;
1753 16         627 $$SEQIDX[$index] = $AUTOKEY++;
1754             }
1755              
1756             sub shift_idx {
1757 3     3 0 6 my $self = shift;
1758 3         7 return shift( @{$self->SEQIDX} );
  3         6  
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         6 my $n = shift;
1765 2         4 my @new;
1766 2         11 push(@new, $AUTOKEY++) for (0..$n-1);
1767 2         4 unshift @{$self->SEQIDX}, @new;
  2         7  
1768 2         7 return @new;
1769             }
1770              
1771             sub rm_idx {
1772 4     4 0 9 my $self = shift;
1773 4         7 my $index = shift;
1774 4 50       6 unless (delete ${$self->SEQIDX}[$index]) {
  4         9  
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   76 use strict;
  6         16  
  6         198  
1785 6     6   41 use warnings;
  6         13  
  6         608  
1786              
1787             # a hashinfo class stub
1788             sub new {
1789 6     6   16 my $class = shift;
1790 6         17 my $self = bless({}, $class);
1791 6         55 $self->{type} = 'HASH';
1792 6         18 return $self;
1793             }
1794              
1795             1;
1796              
1797             package #hide from PAUSE
1798             SQLite_File::BTREEINFO;
1799 6     6   44 use strict;
  6         17  
  6         150  
1800 6     6   31 use warnings;
  6         12  
  6         552  
1801              
1802             # a btreeinfo class stub
1803             sub new {
1804 6     6   13 my $class = shift;
1805 6         13 my $self = bless({}, $class);
1806 6         27 $self->{type} = 'BINARY';
1807 6         17 return $self;
1808             }
1809              
1810             1;
1811              
1812             package #hide from PAUSE
1813             SQLite_File::RECNOINFO;
1814 6     6   49 use strict;
  6         13  
  6         166  
1815 6     6   33 use warnings;
  6         13  
  6         592  
1816              
1817             # a recnoinfo class stub
1818             sub new {
1819 6     6   11 my $class = shift;
1820 6         14 my $self = bless({}, $class);
1821 6         26 $self->{type} = 'RECNO';
1822 6         13 return $self;
1823             }
1824              
1825             1;