Brackup::Dict::SQLite - key-value dictionary implementation, using a


Brackup documentation Contained in the Brackup distribution.

Index


Code Index:

NAME

Top

Brackup::Dict::SQLite - key-value dictionary implementation, using a SQLite database for storage

DESCRIPTION

Top

Brackup::Dict::SQLite implements a simple key-value dictionary using a SQLite database (in a single file) for storage. It provides the default storage backend for both the Brackup::DigestCache digest cache and the Brackup::InventoryDatabase inventory database (as separate databases). The database schema is created automatically as needed - no database maintenance is required.

Brackup::Dict::SQLite is optimised for speed and loads the entire database into memory at startup. If you wish to trade-off some performance for a more conservative memory footprint, you should consider using Brackup::Dict::SQLite2 instead.

See Brackup::DigestCache and Brackup::InventoryDatabase for how to manually specify the dictionary class to use.

DETAILS

Top

File location

The database file location is a parameter defined by the using class, so see Brackup::DigestCache and Brackup::InventoryDatabase for their respective database locations.

SQLite Schema

This is defined automatically, but if you want to look around in it, the schema is:

  CREATE TABLE <TABLE> (
       key TEXT PRIMARY KEY,
       value TEXT
  )

SEE ALSO

Top

brackup

Brackup

Brackup::Dict::SQLite2


Brackup documentation Contained in the Brackup distribution.

package Brackup::Dict::SQLite;
use strict;
use warnings;
use DBI;
use DBD::SQLite;

sub new {
    my ($class, %opts) = @_;
    my $self = bless {
        table => $opts{table},
        file  => $opts{file},
        data  => {},
    }, $class;

    my $dbh = $self->{dbh} = DBI->connect("dbi:SQLite:dbname=$opts{file}","","", { RaiseError => 1, PrintError => 0 }) or
        die "Failed to connect to SQLite filesystem digest cache database at $opts{file}: " . DBI->errstr;

    eval {
        $dbh->do("CREATE TABLE $opts{table} (key TEXT PRIMARY KEY, value TEXT)");
    };
    die "Error: $@" if $@ && $@ !~ /table \w+ already exists/;

    return $self;
}

sub _reset {
    my $self = shift;
    $self->{data} = {};
    $self->{keys} = [];
    $self->{_loaded_keys} = 0;
}

sub _load_all
{
    my $self = shift;
    unless ($self->{_loaded_all}++) {
        # SQLite sucks at doing anything quickly (likes hundred thousand
        # selects back-to-back), so we just suck the whole damn thing into
        # a perl hash.  cute, huh?  then it doesn't have to
        # open/read/seek/seek/seek/read/close for each select later.
        $self->_reset;
        my $sth = $self->{dbh}->prepare("SELECT key, value FROM $self->{table}");
        $sth->execute;
        while (my ($k, $v) = $sth->fetchrow_array) {
            $self->{data}{$k} = $v;
        }
    }
}

sub get {
    my ($self, $key) = @_;
    $self->_load_all unless $self->{_loaded_all};
    return $self->{data}{$key};
}

sub set {
    my ($self, $key, $val) = @_;
    $self->{dbh}->do("REPLACE INTO $self->{table} VALUES (?,?)", undef, $key, $val);
    $self->{data}{$key} = $val;
    return 1;
}

# Iterator interface, returning ($key, $value), and () on eod
sub each {
    my $self = shift;
    $self->_load_all unless $self->{_loaded_all};
    $self->{keys} = [ keys %{$self->{data}} ] unless $self->{_loaded_keys}++;
    if (! @{$self->{keys}}) {
        $self->{_loaded_keys} = 0;
        return wantarray ? () : undef;
    }
    my $next = shift @{$self->{keys}};
    return wantarray ? ($next, $self->{data}{$next}) : $next;
}

sub delete {
    my ($self, $key) = @_;
    $self->{dbh}->do("DELETE FROM $self->{table} WHERE key = ?", undef, $key);
    delete $self->{data}{$key};
    return 1;
}

sub count {
    my $self = shift;
    $self->_load_all unless $self->{_loaded_all};
    return scalar keys %{$self->{data}};
}

sub backing_file {
    my $self = shift;
    return $self->{file};
}

sub wipe {
    die "not implemented";
}

1;

__END__