our
$VERSION
=
'0.3'
;
sub
new
{
my
(
$proto
,
%supplied
) = (
@_
);
my
$class
=
ref
(
$proto
) ||
$proto
;
my
$self
= {};
bless
(
$self
,
$class
);
my
$home
=
$ENV
{
'HOME'
} ||
$ENV
{
'USERPROFILE'
} || (
getpwuid
($<) )[7] ||
"C:/"
;
my
$file
=
$supplied
{
'path'
} ||
"$home/.predis.db"
;
my
$create
= 1;
$create
= 0
if
( -e
$file
);
$self
->{
'db'
} =
DBI->
connect
(
"dbi:SQLite:dbname=$file"
,
""
,
""
, {
AutoCommit
=> 1 } );
if
(
$create
)
{
$self
->{
'db'
}->
do
(
"CREATE TABLE string (id INTEGER PRIMARY KEY, key UNIQUE, val );"
);
$self
->{
'db'
}
->
do
(
"CREATE TABLE sets (id INTEGER PRIMARY KEY, key, val );"
);
}
if
( !
$ENV
{
'SAFE'
} )
{
$self
->{
'db'
}->
do
(
"PRAGMA synchronous = OFF"
);
$self
->{
'db'
}->
do
(
"PRAGMA journal_mode = MEMORY"
);
}
return
$self
;
}
sub
append
{
my
(
$self
,
$key
,
$data
) = (
@_
);
my
$r
=
$self
->get(
$key
);
$r
.=
$data
;
$self
->set(
$key
,
$r
);
}
sub
exists
{
my
(
$self
,
$key
) = (
@_
);
my
$sql
=
$self
->{
'db'
}->prepare(
"SELECT key FROM string WHERE key=?"
);
$sql
->execute(
$key
);
my
$x
=
$sql
->fetchrow_array() ||
undef
;
$sql
->finish();
if
(
$x
)
{
return
1;
}
$sql
=
$self
->{
'db'
}->prepare(
"SELECT key FROM sets WHERE key=?"
);
$sql
->execute(
$key
);
$x
=
$sql
->fetchrow_array() ||
undef
;
$sql
->finish();
if
(
$x
)
{
return
1;
}
return
0;
}
sub
get
{
my
(
$self
,
$key
) = (
@_
);
if
( !
$self
->{
'get'
} )
{
$self
->{
'get'
} =
$self
->{
'db'
}->prepare(
"SELECT val FROM string WHERE key=?"
);
}
$self
->{
'get'
}->execute(
$key
);
my
$x
=
$self
->{
'get'
}->fetchrow_array();
$self
->{
'get'
}->finish();
return
(
$x
);
}
sub
getset
{
my
(
$self
,
$key
,
$val
) = (
@_
);
my
$old
=
$self
->get(
$key
);
$self
->set(
$key
,
$val
);
return
(
$old
);
}
sub
getrange
{
my
(
$self
,
$key
,
$start
,
$end
) = (
@_
);
my
$val
=
$self
->get(
$key
);
my
$s
=
$start
;
my
$e
=
$end
;
if
(
$s
< 0 )
{
$s
=
length
(
$val
) +
$s
;
}
if
(
$e
< 0 )
{
$e
=
length
(
$val
) +
$e
;
}
return
(
substr
(
$val
,
$s
, (
$e
-
$s
+ 1 ) ) );
}
sub
strlen
{
my
(
$self
,
$key
) = (
@_
);
my
$data
=
$self
->get(
$key
);
if
(
defined
(
$data
) )
{
return
(
length
(
$data
) );
}
return
0;
}
sub
rename
{
my
(
$self
,
$key
,
$new_name
) = (
@_
);
$self
->del(
$new_name
);
my
$val
=
$self
->get(
$key
);
$self
->set(
$new_name
,
$val
);
$self
->del(
$key
);
}
sub
renamenx
{
my
(
$self
,
$key
,
$new_name
) = (
@_
);
return
0
if
(
$self
->
exists
(
$new_name
) );
my
$val
=
$self
->get(
$key
);
$self
->set(
$new_name
,
$val
);
$self
->del(
$key
);
return
"OK"
;
}
sub
set
{
my
(
$self
,
$key
,
$val
) = (
@_
);
if
( !
$self
->{
'ins'
} )
{
$self
->{
'ins'
} =
$self
->{
'db'
}
->prepare(
"INSERT OR REPLACE INTO string (key,val) VALUES( ?,? )"
);
}
$self
->{
'ins'
}->execute(
$key
,
$val
);
$self
->{
'ins'
}->finish();
}
sub
setnx
{
my
(
$self
,
$key
,
$val
) = (
@_
);
return
0
if
(
$self
->
exists
(
$key
) );
$self
->set(
$key
,
$val
);
return
1;
}
sub
setrange
{
my
(
$self
,
$key
,
$offset
,
$data
) = (
@_
);
my
$val
=
$self
->get(
$key
);
while
( (
$val
?
length
(
$val
) : 0 ) <
$offset
)
{
$val
.=
chr
(0x00);
}
substr
(
$val
,
$offset
,
length
(
$data
),
$data
);
$self
->set(
$key
,
$val
);
return
(
length
(
$val
) );
}
sub
type
{
my
(
$self
,
$key
) = (
@_
);
my
$sql
=
$self
->{
'db'
}->prepare(
"SELECT key FROM string WHERE key=?"
);
$sql
->execute(
$key
);
my
$x
=
$sql
->fetchrow_array() ||
undef
;
$sql
->finish();
return
'string'
if
(
$x
);
$sql
=
$self
->{
'db'
}->prepare(
"SELECT key FROM sets WHERE key=?"
);
$sql
->execute(
$key
);
$x
=
$sql
->fetchrow_array() ||
undef
;
$sql
->finish();
return
'set'
if
(
$x
);
return
undef
;
}
sub
incr
{
my
(
$self
,
$key
) = (
@_
);
return
(
$self
->incrby(
$key
, 1 ) );
}
sub
incrby
{
my
(
$self
,
$key
,
$amt
) = (
@_
);
$amt
= 1
if
( !
defined
(
$amt
) );
my
$cur
=
$self
->get(
$key
) || 0;
$cur
+=
$amt
;
$self
->set(
$key
,
$cur
);
return
(
$cur
);
}
sub
decr
{
my
(
$self
,
$key
) = (
@_
);
return
(
$self
->decrby(
$key
, 1 ) );
}
sub
decrby
{
my
(
$self
,
$key
,
$amt
) = (
@_
);
$amt
= 1
if
( !
defined
(
$amt
) );
my
$cur
=
$self
->get(
$key
) || 0;
$cur
-=
$amt
;
$self
->set(
$key
,
$cur
);
return
(
$cur
);
}
sub
del
{
my
(
$self
,
$key
) = (
@_
);
my
$str
=
$self
->{
'db'
}->prepare(
"DELETE FROM string WHERE key=?"
);
$str
->execute(
$key
);
$str
->finish();
return
1
if
(
$str
->rows > 0 );
my
$set
=
$self
->{
'db'
}->prepare(
"DELETE FROM sets WHERE key=?"
);
$set
->execute(
$key
);
$set
->finish();
return
1
if
(
$set
->rows > 0 );
return
0;
}
sub
keys
{
my
(
$self
,
$pattern
) = (
@_
);
my
%known
;
foreach
my
$table
(
qw! string sets !
)
{
my
$str
=
$self
->{
'db'
}->prepare(
"SELECT key FROM $table"
);
$str
->execute();
while
(
my
(
$name
) =
$str
->fetchrow_array )
{
$known
{
$name
} += 1;
}
$str
->finish();
}
my
@keys
=
keys
%known
;
if
(
$pattern
)
{
my
@ret
;
foreach
my
$ent
(
@keys
)
{
push
(
@ret
,
$ent
)
if
(
$ent
=~ /
$pattern
/ );
}
return
(
@ret
);
}
else
{
return
(
@keys
);
}
}
sub
randomkey
{
my
(
$self
) = (
@_
);
my
%known
;
foreach
my
$table
(
qw! string sets !
)
{
my
$str
=
$self
->{
'db'
}->prepare(
"SELECT key FROM $table"
);
$str
->execute();
while
(
my
(
$name
) =
$str
->fetchrow_array )
{
$known
{
$name
} += 1;
}
$str
->finish();
}
my
@keys
= CORE::
keys
%known
;
return
(
$keys
[
rand
@keys
] );
}
sub
smembers
{
my
(
$self
,
$key
) = (
@_
);
if
( !
$self
->{
'smembers'
} )
{
$self
->{
'smembers'
} =
$self
->{
'db'
}->prepare(
"SELECT val FROM sets WHERE key=?"
);
}
$self
->{
'smembers'
}->execute(
$key
);
my
@vals
;
while
(
my
(
$name
) =
$self
->{
'smembers'
}->fetchrow_array )
{
push
(
@vals
,
$name
);
}
$self
->{
'smembers'
}->finish();
return
(
@vals
);
}
sub
smove
{
my
(
$self
,
$src
,
$dst
,
$ent
) = (
@_
);
my
$sql
=
$self
->{
'db'
}
->prepare(
"UPDATE sets SET key=? WHERE ( key=? AND val=?)"
);
$sql
->execute(
$dst
,
$src
,
$ent
);
$sql
->finish();
if
(
$sql
->rows > 0 )
{
return
1;
}
return
0;
}
sub
sismember
{
my
(
$self
,
$set
,
$key
) = (
@_
);
my
$sql
=
$self
->{
'db'
}->prepare(
"SELECT val FROM sets WHERE key=? AND val=?"
);
$sql
->execute(
$set
,
$key
);
my
$x
=
$sql
->fetchrow_array() ||
undef
;
$sql
->finish();
if
(
defined
(
$x
) && (
$x
eq
$key
) )
{
return
1;
}
return
0;
}
sub
sadd
{
my
(
$self
,
$key
,
$val
) = (
@_
);
if
( !
$self
->{
'sadd'
} )
{
$self
->{
'sadd'
} =
$self
->{
'db'
}->prepare(
"INSERT INTO sets (key,val) SELECT ?,? WHERE NOT EXISTS( SELECT key, val FROM sets WHERE key=? AND val=? );"
);
}
$self
->{
'sadd'
}->execute(
$key
,
$val
,
$key
,
$val
);
$self
->{
'sadd'
}->finish();
if
(
$self
->{
'sadd'
}->rows > 0 )
{
return
1;
}
return
0;
}
sub
srem
{
my
(
$self
,
$key
,
$val
) = (
@_
);
if
( !
$self
->{
'srem'
} )
{
$self
->{
'srem'
} =
$self
->{
'db'
}->prepare(
"DELETE FROM sets WHERE (key=? AND val=?)"
);
}
$self
->{
'srem'
}->execute(
$key
,
$val
);
$self
->{
'srem'
}->finish();
if
(
$self
->{
'srem'
}->rows > 0 )
{
return
1;
}
return
0;
}
sub
spop
{
my
(
$self
,
$key
,
$count
) = (
@_
);
$count
= 1
if
( !
defined
(
$count
) );
my
@res
;
while
( (
$count
> 0 ) && (
$count
<=
$self
->scard(
$key
) ) )
{
my
$rand
=
$self
->srandmember(
$key
);
push
(
@res
,
$rand
);
$self
->srem(
$key
,
$rand
);
$count
-= 1;
}
return
(
@res
);
}
sub
srandmember
{
my
(
$self
,
$key
) = (
@_
);
if
( !
$self
->{
'srandommember'
} )
{
$self
->{
'srandommember'
} =
$self
->{
'db'
}->prepare(
"SELECT val FROM sets where key=? ORDER BY RANDOM() LIMIT 1"
) or
die
"Failed to prepare"
;
}
$self
->{
'srandommember'
}->execute(
$key
);
my
$x
=
$self
->{
'srandommember'
}->fetchrow_array() ||
""
;
$self
->{
'srandommember'
}->finish();
return
(
$x
);
}
sub
sunion
{
my
(
$self
,
@keys
) = (
@_
);
my
%result
;
foreach
my
$key
(
@keys
)
{
my
@vals
=
$self
->smembers(
$key
);
foreach
my
$val
(
@vals
)
{
$result
{
$val
} += 1;
}
}
return
( CORE::
keys
(
%result
) );
}
sub
sunionstore
{
my
(
$self
,
$dest
,
@keys
) = (
@_
);
my
@union
=
$self
->sunion(
@keys
);
$self
->del(
$dest
);
foreach
my
$ent
(
@union
)
{
$self
->sadd(
$dest
,
$ent
);
}
return
(
scalar
@union
);
}
sub
sinter
{
my
(
$self
,
@names
) = (
@_
);
my
%seen
;
foreach
my
$key
(
@names
)
{
my
@vals
=
$self
->smembers(
$key
);
foreach
my
$val
(
@vals
)
{
$seen
{
$val
} += 1;
}
}
my
@result
;
foreach
my
$key
( CORE::
keys
(
%seen
) )
{
if
(
$seen
{
$key
} ==
scalar
@names
)
{
push
(
@result
,
$key
);
}
}
return
(
@result
);
}
sub
sinterstore
{
my
(
$self
,
$dest
,
@names
) = (
@_
);
my
@update
=
$self
->sinter(
@names
);
$self
->del(
$dest
);
foreach
my
$ent
(
@update
)
{
$self
->sadd(
$dest
,
$ent
);
}
return
(
scalar
@update
);
}
sub
scard
{
my
(
$self
,
$key
) = (
@_
);
if
( !
$self
->{
'scard'
} )
{
$self
->{
'scard'
} =
$self
->{
'db'
}->prepare(
"SELECT COUNT(id) FROM sets where key=?"
);
}
$self
->{
'scard'
}->execute(
$key
);
my
$count
=
$self
->{
'scard'
}->fetchrow_array() || 0;
$self
->{
'scard'
}->finish();
return
(
$count
);
}
sub
bitcount
{
my
(
$self
,
$key
) = (
@_
);
my
$val
=
$self
->get(
$key
);
my
@bitcounts
= ( 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 1, 2,
2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 1, 2, 2, 3,
2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4,
4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 1, 2, 2, 3, 2, 3, 3, 4,
2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4,
4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5,
4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6,
6, 7, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3,
3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5,
4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 2, 3, 3, 4, 3, 4,
4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6,
4, 5, 5, 6, 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5,
5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7,
6, 7, 7, 8
);
my
$sum
= 0;
foreach
my
$char
(
split
( //,
$val
) )
{
$sum
+=
$bitcounts
[
ord
(
$char
)];
}
return
(
$sum
);
}
sub
setbit
{
my
(
$self
,
$key
,
$offset
,
$value
) = (
@_
);
my
$val
=
$self
->get(
$key
) ||
""
;
my
$len
=
length
(
$val
) || 0;
my
$bin
;
foreach
my
$byte
(
split
( //,
$val
) )
{
$bin
.=
unpack
(
"B*"
,
$byte
);
}
while
(
$offset
>= (
$bin
?
length
(
$bin
) : 0 ) )
{
$bin
.=
"00000000"
;
}
substr
(
$bin
,
$offset
, 1,
$value
);
my
$updated
;
while
(
length
(
$bin
) )
{
my
$next
=
substr
(
$bin
, 0, 8 );
$bin
=
substr
(
$bin
, 8 );
$updated
.=
pack
(
"B*"
,
$next
);
}
$self
->set(
$key
,
$updated
);
}
sub
getbit
{
my
(
$self
,
$key
,
$offset
) = (
@_
);
my
$val
=
$self
->get(
$key
) ||
""
;
my
$len
=
length
(
$val
) || 0;
my
$bin
;
foreach
my
$byte
(
split
( //,
$val
) )
{
$bin
.=
unpack
(
"B*"
,
$byte
);
}
while
(
$offset
>= (
$bin
?
length
(
$bin
) : 0 ) )
{
$bin
.=
"00000000"
;
}
return
(
substr
(
$bin
,
$offset
, 1 ) );
}
sub
ping
{
return
1;
}
sub
echo
{
my
(
$self
,
$arg
) = (
@_
);
return
(
$arg
);
}
our
$AUTOLOAD
;
sub
AUTOLOAD
{
my
$command
=
$AUTOLOAD
;
$command
=~ s/.*://;
warn
"NOT IMPLEMENTED:$command"
;
return
1;
}
sub
mget
{
my
(
$self
,
@keys
) = (
@_
);
my
@ret
;
foreach
my
$key
(
@keys
)
{
if
(
$self
->
exists
(
$key
) )
{
push
(
@ret
,
$self
->get(
$key
) );
}
else
{
push
(
@ret
,
undef
);
}
}
return
(
@ret
);
}
sub
mset
{
my
(
$self
,
@keys
) = (
@_
);
while
(
scalar
@keys
)
{
my
(
$key
,
$val
) =
splice
(
@keys
, 0, 2 );
$self
->set(
$key
,
$val
);
}
}
sub
msetnx
{
my
(
$self
,
@keys
) = (
@_
);
my
%hash
;
while
(
scalar
@keys
)
{
my
(
$key
,
$val
) =
splice
(
@keys
, 0, 2 );
$hash
{
$key
} =
$val
;
}
foreach
my
$key
( CORE::
keys
%hash
)
{
return
0
if
(
$self
->
exists
(
$key
) );
}
foreach
my
$key
( CORE::
keys
%hash
)
{
$self
->set(
$key
,
$hash
{
$key
} );
}
return
1;
}
1;