File Coverage

blib/lib/Bot/BasicBot/Pluggable/Store/DBI.pm
Criterion Covered Total %
statement 106 113 93.8
branch 20 36 55.5
condition 6 9 66.6
subroutine 18 19 94.7
pod 6 9 66.6
total 156 186 83.8


line stmt bran cond sub pod time code
1             package Bot::BasicBot::Pluggable::Store::DBI;
2             $Bot::BasicBot::Pluggable::Store::DBI::VERSION = '1.20';
3 1     1   4 use warnings;
  1         1  
  1         31  
4 1     1   3 use strict;
  1         0  
  1         19  
5 1     1   3 use Carp qw( croak );
  1         0  
  1         70  
6 1     1   537 use Data::Dumper;
  1         4787  
  1         66  
7 1     1   1189 use DBI;
  1         11973  
  1         55  
8 1     1   655 use Storable qw( nfreeze thaw );
  1         2419  
  1         63  
9 1     1   457 use Try::Tiny;
  1         1531  
  1         48  
10              
11 1     1   5 use base qw( Bot::BasicBot::Pluggable::Store );
  1         1  
  1         440  
12              
13             sub init {
14 1     1 1 1 my $self = shift;
15 1   50     5 $self->{dsn} ||= 'dbi:SQLite:bot-basicbot.sqlite';
16 1   50     3 $self->{table} ||= 'basicbot';
17 1         3 $self->create_table;
18             }
19              
20             sub dbh {
21 29     29 0 41 my $self = shift;
22 29 50       75 my $dsn = $self->{dsn} or die "I need a DSN";
23 29         40 my $user = $self->{user};
24 29         31 my $password = $self->{password};
25 29         118 return DBI->connect_cached( $dsn, $user, $password );
26             }
27              
28             sub create_table {
29 1     1 0 2 my $self = shift;
30 1 50       28 my $table = $self->{table} or die "Need DB table";
31 1         2 my $sth = $self->dbh->table_info( '', '', $table, "TABLE" );
32              
33 1         10053 $table = $self->dbh->quote_identifier($table);
34              
35 1 50       259 if ( !$sth->fetch ) {
36 1         3 $self->dbh->do(
37             "CREATE TABLE $table (
38             id INT PRIMARY KEY,
39             namespace TEXT,
40             store_key TEXT,
41             store_value LONGBLOB )"
42             );
43 1 50       585969 if ( $self->{create_index} ) {
44             try {
45 0     0   0 $self->dbh->do(
46             "CREATE INDEX lookup ON $table ( namespace(10), store_key(10) )"
47             );
48 0         0 };
49             }
50             }
51             }
52              
53             sub get {
54 4     4 1 12 my ( $self, $namespace, $key ) = @_;
55 4 50       22 my $table = $self->{table} or die "Need DB table";
56              
57 4         15 $table = $self->dbh->quote_identifier($table);
58              
59 4         1096 my $sth = $self->dbh->prepare_cached(
60             "SELECT store_value FROM $table WHERE namespace=? and store_key=?");
61 4         1392 $sth->execute( $namespace, $key );
62 4         44 my $row = $sth->fetchrow_arrayref;
63 4         14 $sth->finish;
64 4 100 66     36 return unless $row and @$row;
65 1     1   14 return try { thaw( $row->[0] ) } catch { $row->[0] };
  1         75  
  1         333  
66             }
67              
68             sub set {
69 2     2 1 8 my ( $self, $namespace, $key, $value ) = @_;
70 2 50       10 my $table = $self->{table} or die "Need DB table";
71              
72 2         7 $table = $self->dbh->quote_identifier($table);
73              
74 2 50       606 $value = nfreeze($value) if ref($value);
75 2 50       9 if ( defined( $self->get( $namespace, $key ) ) ) {
76 0         0 my $sth = $self->dbh->prepare_cached(
77             "UPDATE $table SET store_value=? WHERE namespace=? AND store_key=?"
78             );
79 0         0 $sth->execute( $value, $namespace, $key );
80 0         0 $sth->finish;
81             }
82             else {
83 2         7 my $sth = $self->dbh->prepare_cached(
84             "INSERT INTO $table (id, store_value, namespace, store_key) VALUES (?, ?, ?, ?)"
85             );
86 2         535 $sth->execute( $self->new_id($table), $value, $namespace, $key );
87 2         51 $sth->finish;
88             }
89 2         49 return $self;
90             }
91              
92             sub unset {
93 1     1 1 3 my ( $self, $namespace, $key ) = @_;
94 1 50       10 my $table = $self->{table} or die "Need DB table";
95              
96 1         4 $table = $self->dbh->quote_identifier($table);
97              
98 1         236 my $sth = $self->dbh->prepare_cached(
99             "DELETE FROM $table WHERE namespace=? and store_key=?");
100 1         7840 $sth->execute( $namespace, $key );
101 1         51 $sth->finish;
102             }
103              
104             sub new_id {
105 2     2 0 4 my $self = shift;
106 2         20 my $table = shift;
107 2         10 my $sth = $self->dbh->prepare_cached("SELECT MAX(id) FROM $table");
108 2         671 $sth->execute();
109 2   100     43 my $id = $sth->fetchrow_arrayref->[0] || "0";
110 2         11 $sth->finish();
111 2         36553 return $id + 1;
112             }
113              
114             sub keys {
115 4     4 1 16 my ( $self, $namespace, %opts ) = @_;
116 4 50       21 my $table = $self->{table} or die "Need DB table";
117              
118 4         17 $table = $self->dbh->quote_identifier($table);
119              
120 4 100       1440 my @res = ( exists $opts{res} ) ? @{ $opts{res} } : ();
  1         3  
121              
122 4         14 my $sql = "SELECT store_key FROM $table WHERE namespace=?";
123              
124 4         10 my @args = ($namespace);
125              
126 4         11 foreach my $re (@res) {
127 1         3 my $orig = $re;
128              
129             # h-h-h-hack .... convert to SQL and limit terms if too general
130 1 50       8 $re = "%$re" if $re !~ s!^\^!!;
131 1 50       6 $re = "$re%" if $re !~ s!\$$!!;
132 1 50       7 $re = "${namespace}_${re}" if $orig =~ m!^[^\^].*[^\$]$!;
133              
134 1         4 $sql .= " AND store_key LIKE ?";
135 1         4 push @args, $re;
136             }
137 4 50       18 if ( exists $opts{limit} ) {
138 0         0 $sql .= " LIMIT ?";
139 0         0 push @args, $opts{limit};
140             }
141              
142 4         13 my $sth = $self->dbh->prepare_cached($sql);
143 4         1606 $sth->execute(@args);
144              
145 4 50       24 return $sth->rows if $opts{_count_only};
146              
147 4         10 my @keys = map { $_->[0] } @{ $sth->fetchall_arrayref };
  4         18  
  4         86  
148 4         24 $sth->finish;
149 4         36 return @keys;
150             }
151              
152             sub namespaces {
153 1     1 1 3 my ($self) = @_;
154 1 50       7 my $table = $self->{table} or die "Need DB table";
155              
156 1         5 $table = $self->dbh->quote_identifier($table);
157              
158 1         237 my $sth =
159             $self->dbh->prepare_cached("SELECT DISTINCT namespace FROM $table");
160 1         718 $sth->execute();
161 1         3 my @keys = map { $_->[0] } @{ $sth->fetchall_arrayref };
  1         7  
  1         82  
162 1         6 $sth->finish;
163 1         8 return @keys;
164             }
165              
166             1;
167              
168             __END__
169              
170             =head1 NAME
171              
172             Bot::BasicBot::Pluggable::Store::DBI - use DBI to provide a storage backend
173              
174             =head1 VERSION
175              
176             version 1.20
177              
178             =head1 SYNOPSIS
179              
180             my $store = Bot::BasicBot::Pluggable::Store::DBI->new(
181             dsn => "dbi:mysql:bot",
182             user => "user",
183             password => "password",
184             table => "brane",
185              
186             # create indexes on key/values?
187             create_index => 1,
188             );
189              
190             $store->set( "namespace", "key", "value" );
191            
192             =head1 DESCRIPTION
193              
194             This is a L<Bot::BasicBot::Pluggable::Store> that uses a database to store
195             the values set by modules. Complex values are stored using Storable.
196              
197             =head1 AUTHOR
198              
199             Mario Domgoergen <mdom@cpan.org>
200              
201             This program is free software; you can redistribute it
202             and/or modify it under the same terms as Perl itself.