Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
ruanhaishen
redis
Commits
f78fd11b
Commit
f78fd11b
authored
Mar 25, 2009
by
antirez
Browse files
New file dump format, perl client library added
parent
7b45bfb2
Changes
17
Hide whitespace changes
Inline
Side-by-side
TODO
View file @
f78fd11b
...
@@ -7,3 +7,6 @@
...
@@ -7,3 +7,6 @@
- maxclients directive
- maxclients directive
- check 'server.dirty' everywere
- check 'server.dirty' everywere
- replication automated tests
- replication automated tests
- a command, or an external tool, to perform the MD5SUM of the whole dataset, so that if the dataset between two servers is identical, so will be the MD5SUM
* Include Lua and Perl bindings
client-libraries/README
View file @
f78fd11b
...
@@ -9,9 +9,9 @@ code or recent bugfixes read more.
...
@@ -9,9 +9,9 @@ code or recent bugfixes read more.
How to get the lastest versions of client libraries source code
How to get the lastest versions of client libraries source code
---------------------------------------------------------------
---------------------------------------------------------------
Note that while the
PHP
and Python
version
s are the most uptodate
available
Note that while the
pure PHP, Tcl
and Python
librarie
s are the most uptodate
libraries,
the Ruby and Erlang
libraries have their own
sites so you may want
available
libraries,
all the other
libraries have their own
repositories where
to grab this libraries from their main sites
:
it's possible to grab the most recent version
:
Ruby lib source code:
Ruby lib source code:
http://github.com/ezmobius/redis-rb/tree/master
http://github.com/ezmobius/redis-rb/tree/master
...
@@ -19,10 +19,11 @@ http://github.com/ezmobius/redis-rb/tree/master
...
@@ -19,10 +19,11 @@ http://github.com/ezmobius/redis-rb/tree/master
Erlang lib source code:
Erlang lib source code:
http://bitbucket.org/adroll/erldis/
http://bitbucket.org/adroll/erldis/
For the languages with development code in the Redis SVN, check this urls for unstable versions of the libs:
Perl lib source code:
(web) http://svn.rot13.org/index.cgi/Redis
(svn) svn://svn.rot13.org/Redis/
Python lib source cod
e:
Redis-php PHP C modul
e:
http://code.google.com/p/redis/
source/browse/#svn/trunk/client-libraries/python
http://code.google.com/p/
php
redis/
PHP lib source code:
For all the rest check the Redis tarball or Git repository.
http://code.google.com/p/redis/source/browse/#svn/trunk/client-libraries/php
client-libraries/perl/Changes
0 → 100644
View file @
f78fd11b
Revision history for Redis
0.01 Sun Mar 22 19:02:17 CET 2009
First version, tracking git://github.com/antirez/redis
0.08 Tue Mar 24 22:38:59 CET 2009
This version supports new protocol introduced in beta 8
Version bump to be in-sync with Redis version
client-libraries/perl/MANIFEST
0 → 100644
View file @
f78fd11b
Changes
MANIFEST
Makefile.PL
README
lib/Redis.pm
t/00-load.t
t/pod-coverage.t
t/pod.t
client-libraries/perl/Makefile.PL
0 → 100644
View file @
f78fd11b
use
strict;
use
warnings;
use ExtUtils
::
MakeMaker;
WriteMakefile(
NAME
=>
'Redis'
,
AUTHOR
=>
'Dobrica Pavlinusic <dpavlin@rot13.org>'
,
VERSION_FROM
=>
'lib/Redis.pm'
,
ABSTRACT_FROM
=>
'lib/Redis.pm'
,
PL_FILES
=>
{}
,
PREREQ_PM
=>
{
'Test::More'
=>
0,
'IO::Socket::INET'
=>
0,
'Data::Dump'
=>
0,
'Carp'
=>
0,
},
dist
=>
{
COMPRESS
=>
'gzip -9f'
, SUFFIX
=>
'gz'
,
}
,
clean
=>
{
FILES
=>
'Redis-*'
}
,
);
client-libraries/perl/README
0 → 100644
View file @
f78fd11b
Redis
Perl binding for Redis database which is in-memory hash store with
support for scalars, arrays and sets and disk persistence.
INSTALLATION
To install this module, run the following commands:
perl Makefile.PL
make
make test
make install
SUPPORT AND DOCUMENTATION
After installing, you can find documentation for this module with the
perldoc command.
perldoc Redis
You can also look for information at:
RT, CPAN's request tracker
http://rt.cpan.org/NoAuth/Bugs.html?Dist=Redis
AnnoCPAN, Annotated CPAN documentation
http://annocpan.org/dist/Redis
CPAN Ratings
http://cpanratings.perl.org/d/Redis
Search CPAN
http://search.cpan.org/dist/Redis
COPYRIGHT AND LICENCE
Copyright (C) 2009 Dobrica Pavlinusic
This program is free software; you can redistribute it and/or modify it
under the same terms as Perl itself.
client-libraries/perl/lib/Redis.pm
0 → 100644
View file @
f78fd11b
package
Redis
;
use
warnings
;
use
strict
;
use
IO::Socket::
INET
;
use
Data::
Dump
qw/dump/
;
use
Carp
qw/confess/
;
=head1 NAME
Redis - perl binding for Redis database
=cut
our
$VERSION
=
'
0.08
';
=head1 DESCRIPTION
Pure perl bindings for L<http://code.google.com/p/redis/>
This version support git version 0.08 of Redis available at
L<git://github.com/antirez/redis>
This documentation
lists commands which are exercised in test suite, but
additinal commands will work correctly since protocol
specifies enough information to support almost all commands
with same peace of code with a little help of C<AUTOLOAD>.
=head1 FUNCTIONS
=head2 new
my $r = Redis->new;
=cut
our
$debug
=
$ENV
{
REDIS
}
||
0
;
our
$sock
;
my
$server
=
'
127.0.0.1:6379
';
sub
new
{
my
$class
=
shift
;
my
$self
=
{};
bless
(
$self
,
$class
);
warn
"
# opening socket to
$server
";
$sock
||=
IO::Socket::
INET
->
new
(
PeerAddr
=>
$server
,
Proto
=>
'
tcp
',
)
||
die
$!
;
$self
;
}
my
$bulk_command
=
{
set
=>
1
,
setnx
=>
1
,
rpush
=>
1
,
lpush
=>
1
,
lset
=>
1
,
lrem
=>
1
,
sadd
=>
1
,
srem
=>
1
,
sismember
=>
1
,
echo
=>
1
,
};
# we don't want DESTROY to fallback into AUTOLOAD
sub
DESTROY
{}
our
$AUTOLOAD
;
sub
AUTOLOAD
{
my
$self
=
shift
;
my
$command
=
$AUTOLOAD
;
$command
=~
s/.*://
;
warn
"
##
$command
",
dump
(
@
_
)
if
$debug
;
my
$send
;
if
(
defined
$bulk_command
->
{
$command
}
)
{
my
$value
=
pop
;
$value
=
''
if
!
defined
$value
;
$send
=
uc
(
$command
)
.
'
'
.
join
('
',
@
_
)
.
'
'
.
length
(
$value
)
.
"
\r\n
$value
\r\n
"
;
}
else
{
$send
=
uc
(
$command
)
.
'
'
.
join
('
',
@
_
)
.
"
\r\n
"
;
}
warn
"
>>
$send
"
if
$debug
;
print
$sock
$send
;
if
(
$command
eq
'
quit
'
)
{
close
(
$sock
)
||
die
"
can't close socket: $!
";
return
1
;
}
my
$result
=
<
$sock
>
||
die
"
can't read socket: $!
";
warn
"
<<
$result
"
if
$debug
;
my
$type
=
substr
(
$result
,
0
,
1
);
$result
=
substr
(
$result
,
1
,
-
2
);
if
(
$command
eq
'
info
'
)
{
my
$hash
;
foreach
my
$l
(
split
(
/\r\n/
,
__sock_read_bulk
(
$result
)
)
)
{
my
(
$n
,
$v
)
=
split
(
/:/
,
$l
,
2
);
$hash
->
{
$n
}
=
$v
;
}
return
$hash
;
}
elsif
(
$command
eq
'
keys
'
)
{
my
$keys
=
__sock_read_bulk
(
$result
);
return
split
(
/\s/
,
$keys
)
if
$keys
;
return
;
}
if
(
$type
eq
'
-
'
)
{
confess
$result
;
}
elsif
(
$type
eq
'
+
'
)
{
return
$result
;
}
elsif
(
$type
eq
'
$
'
)
{
return
__sock_read_bulk
(
$result
);
}
elsif
(
$type
eq
'
*
'
)
{
return
__sock_read_multi_bulk
(
$result
);
}
elsif
(
$type
eq
'
:
'
)
{
return
$result
;
# FIXME check if int?
}
else
{
confess
"
unknown type:
$type
",
__sock_read_line
();
}
}
sub
__sock_read_bulk
{
my
$len
=
shift
;
return
undef
if
$len
<
0
;
my
$v
;
if
(
$len
>
0
)
{
read
(
$sock
,
$v
,
$len
)
||
die
$!
;
warn
"
<<
",
dump
(
$v
),
$/
if
$debug
;
}
my
$crlf
;
read
(
$sock
,
$crlf
,
2
);
# skip cr/lf
return
$v
;
}
sub
__sock_read_multi_bulk
{
my
$size
=
shift
;
return
undef
if
$size
<
0
;
$size
--
;
my
@list
=
(
0
..
$size
);
foreach
(
0
..
$size
)
{
$list
[
$_
]
=
__sock_read_bulk
(
substr
(
<
$sock
>
,
1
,
-
2
)
);
}
warn
"
## list =
",
dump
(
@list
)
if
$debug
;
return
@list
;
}
1
;
__END__
=head1 Connection Handling
=head2 quit
$r->quit;
=head2 ping
$r->ping || die "no server?";
=head1 Commands operating on string values
=head2 set
$r->set( foo => 'bar' );
$r->setnx( foo => 42 );
=head2 get
my $value = $r->get( 'foo' );
=head2 mget
my @values = $r->mget( 'foo', 'bar', 'baz' );
=head2 incr
$r->incr('counter');
$r->incrby('tripplets', 3);
=head2 decr
$r->decr('counter');
$r->decrby('tripplets', 3);
=head2 exists
$r->exists( 'key' ) && print "got key!";
=head2 del
$r->del( 'key' ) || warn "key doesn't exist";
=head2 type
$r->type( 'key' ); # = string
=head1 Commands operating on the key space
=head2 keys
my @keys = $r->keys( '*glob_pattern*' );
=head2 randomkey
my $key = $r->randomkey;
=head2 rename
my $ok = $r->rename( 'old-key', 'new-key', $new );
=head2 dbsize
my $nr_keys = $r->dbsize;
=head1 Commands operating on lists
See also L<Redis::List> for tie interface.
=head2 rpush
$r->rpush( $key, $value );
=head2 lpush
$r->lpush( $key, $value );
=head2 llen
$r->llen( $key );
=head2 lrange
my @list = $r->lrange( $key, $start, $end );
=head2 ltrim
my $ok = $r->ltrim( $key, $start, $end );
=head2 lindex
$r->lindex( $key, $index );
=head2 lset
$r->lset( $key, $index, $value );
=head2 lrem
my $modified_count = $r->lrem( $key, $count, $value );
=head2 lpop
my $value = $r->lpop( $key );
=head2 rpop
my $value = $r->rpop( $key );
=head1 Commands operating on sets
=head2 sadd
$r->sadd( $key, $member );
=head2 srem
$r->srem( $key, $member );
=head2 scard
my $elements = $r->scard( $key );
=head2 sismember
$r->sismember( $key, $member );
=head2 sinter
$r->sinter( $key1, $key2, ... );
=head2 sinterstore
my $ok = $r->sinterstore( $dstkey, $key1, $key2, ... );
=head1 Multiple databases handling commands
=head2 select
$r->select( $dbindex ); # 0 for new clients
=head2 move
$r->move( $key, $dbindex );
=head2 flushdb
$r->flushdb;
=head2 flushall
$r->flushall;
=head1 Sorting
=head2 sort
$r->sort("key BY pattern LIMIT start end GET pattern ASC|DESC ALPHA');
=head1 Persistence control commands
=head2 save
$r->save;
=head2 bgsave
$r->bgsave;
=head2 lastsave
$r->lastsave;
=head2 shutdown
$r->shutdown;
=head1 Remote server control commands
=head2 info
my $info_hash = $r->info;
=head1 AUTHOR
Dobrica Pavlinusic, C<< <dpavlin at rot13.org> >>
=head1 BUGS
Please report any bugs or feature requests to C<bug-redis at rt.cpan.org>, or through
the web interface at L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Redis>. I will be notified, and then you'll
automatically be notified of progress on your bug as I make changes.
=head1 SUPPORT
You can find documentation for this module with the perldoc command.
perldoc Redis
perldoc Redis::List
perldoc Redis::Hash
You can also look for information at:
=over 4
=item * RT: CPAN's request tracker
L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=Redis>
=item * AnnoCPAN: Annotated CPAN documentation
L<http://annocpan.org/dist/Redis>
=item * CPAN Ratings
L<http://cpanratings.perl.org/d/Redis>
=item * Search CPAN
L<http://search.cpan.org/dist/Redis>
=back
=head1 ACKNOWLEDGEMENTS
=head1 COPYRIGHT & LICENSE
Copyright 2009 Dobrica Pavlinusic, all rights reserved.
This program is free software; you can redistribute it and/or modify it
under the same terms as Perl itself.
=cut
1; # End of Redis
client-libraries/perl/lib/Redis/Hash.pm
0 → 100644
View file @
f78fd11b
package
Redis::
Hash
;
use
strict
;
use
warnings
;
use
Tie::
Hash
;
use
base
qw/Redis Tie::StdHash/
;
use
Data::
Dump
qw/dump/
;
=head1 NAME
Redis::Hash - tie perl hashes into Redis
=head1 SYNOPSYS
tie %name, 'Redis::Hash', 'prefix';
=cut
# mandatory methods
sub
TIEHASH
{
my
(
$class
,
$name
)
=
@_
;
my
$self
=
Redis
->
new
;
$name
.=
'
:
'
if
$name
;
$self
->
{
name
}
=
$name
||
'';
bless
$self
=>
$class
;
}
sub
STORE
{
my
(
$self
,
$key
,
$value
)
=
@_
;
$self
->
set
(
$self
->
{
name
}
.
$key
,
$value
);
}
sub
FETCH
{
my
(
$self
,
$key
)
=
@_
;
$self
->
get
(
$self
->
{
name
}
.
$key
);
}
sub
FIRSTKEY
{
my
$self
=
shift
;
$self
->
{
keys
}
=
[
$self
->
keys
(
$self
->
{
name
}
.
'
*
'
)
];
$self
->
NEXTKEY
;
}
sub
NEXTKEY
{
my
$self
=
shift
;
my
$key
=
shift
@
{
$self
->
{
keys
}
}
||
return
;
my
$name
=
$self
->
{
name
};
$key
=~
s{^$name}{}
||
warn
"
can't strip
$name
from
$key
";
return
$key
;
}
sub
EXISTS
{
my
(
$self
,
$key
)
=
@_
;
$self
->
exists
(
$self
->
{
name
}
.
$key
);
}
sub
DELETE
{
my
(
$self
,
$key
)
=
@_
;
$self
->
del
(
$self
->
{
name
}
.
$key
);
}
sub
CLEAR
{
my
(
$self
)
=
@_
;
$self
->
del
(
$_
)
foreach
(
$self
->
keys
(
$self
->
{
name
}
.
'
*
'
)
);
$self
->
{
keys
}
=
[]
;
}
1
;
client-libraries/perl/lib/Redis/List.pm
0 → 100644
View file @
f78fd11b
package
Redis::
List
;
use
strict
;
use
warnings
;
use
base
qw/Redis Tie::Array/
;
=head1 NAME
Redis::List - tie perl arrays into Redis lists
=head1 SYNOPSYS
tie @a, 'Redis::List', 'name';
=cut
# mandatory methods
sub
TIEARRAY
{
my
(
$class
,
$name
)
=
@_
;
my
$self
=
$class
->
new
;
$self
->
{
name
}
=
$name
;
bless
$self
=>
$class
;
}
sub
FETCH
{
my
(
$self
,
$index
)
=
@_
;
$self
->
lindex
(
$self
->
{
name
},
$index
);
}
sub
FETCHSIZE
{
my
(
$self
)
=
@_
;
$self
->
llen
(
$self
->
{
name
}
);
}
sub
STORE
{
my
(
$self
,
$index
,
$value
)
=
@_
;
$self
->
lset
(
$self
->
{
name
},
$index
,
$value
);
}
sub
STORESIZE
{
my
(
$self
,
$count
)
=
@_
;
$self
->
ltrim
(
$self
->
{
name
},
0
,
$count
);
# if $count > $self->FETCHSIZE;
}
sub
CLEAR
{
my
(
$self
)
=
@_
;
$self
->
del
(
$self
->
{
name
}
);
}
sub
PUSH
{
my
$self
=
shift
;
$self
->
rpush
(
$self
->
{
name
},
$_
)
foreach
@_
;
}
sub
SHIFT
{
my
$self
=
shift
;
$self
->
lpop
(
$self
->
{
name
}
);
}
sub
UNSHIFT
{
my
$self
=
shift
;
$self
->
lpush
(
$self
->
{
name
},
$_
)
foreach
@_
;
}
sub
SPLICE
{
my
$self
=
shift
;
my
$offset
=
shift
;
my
$length
=
shift
;
$self
->
lrange
(
$self
->
{
name
},
$offset
,
$length
);
# FIXME rest of @_ ?
}
sub
EXTEND
{
my
(
$self
,
$count
)
=
@_
;
$self
->
rpush
(
$self
->
{
name
},
''
)
foreach
(
$self
->
FETCHSIZE
..
(
$count
-
1
)
);
}
sub
DESTROY
{
my
$self
=
shift
;
$self
->
quit
;
}
1
;
client-libraries/perl/scripts/redis-benchmark.pl
0 → 100755
View file @
f78fd11b
#!/usr/bin/perl
use
warnings
;
use
strict
;
use
Benchmark
qw/:all/
;
use
lib
'
lib
';
use
Redis
;
my
$r
=
Redis
->
new
;
my
$i
=
0
;
timethese
(
100000
,
{
'
00_ping
'
=>
sub
{
$r
->
ping
},
'
10_set
'
=>
sub
{
$r
->
set
(
'
foo
',
$i
++
)
},
'
11_set_r
'
=>
sub
{
$r
->
set
(
'
bench-
'
.
rand
(),
rand
()
)
},
'
20_get
'
=>
sub
{
$r
->
get
(
'
foo
'
)
},
'
21_get_r
'
=>
sub
{
$r
->
get
(
'
bench-
'
.
rand
()
)
},
'
30_incr
'
=>
sub
{
$r
->
incr
(
'
counter
'
)
},
'
30_incr_r
'
=>
sub
{
$r
->
incr
(
'
bench-
'
.
rand
()
)
},
'
40_lpush
'
=>
sub
{
$r
->
lpush
(
'
mylist
',
'
bar
'
)
},
'
40_lpush
'
=>
sub
{
$r
->
lpush
(
'
mylist
',
'
bar
'
)
},
'
50_lpop
'
=>
sub
{
$r
->
lpop
(
'
mylist
'
)
},
});
client-libraries/perl/t/00-load.t
0 → 100644
View file @
f78fd11b
#!perl -T
use
Test::
More
tests
=>
1
;
BEGIN
{
use_ok
(
'
Redis
'
);
}
diag
(
"
Testing Redis
$Redis
::VERSION, Perl $], $^X
"
);
client-libraries/perl/t/01-Redis.t
0 → 100755
View file @
f78fd11b
#!/usr/bin/perl
use
warnings
;
use
strict
;
use
Test::
More
tests
=>
106
;
use
Data::
Dump
qw/dump/
;
use
lib
'
lib
';
BEGIN
{
use_ok
(
'
Redis
'
);
}
ok
(
my
$o
=
Redis
->
new
(),
'
new
'
);
ok
(
$o
->
ping
,
'
ping
'
);
diag
"
Commands operating on string values
";
ok
(
$o
->
set
(
foo
=>
'
bar
'
),
'
set foo => bar
'
);
ok
(
!
$o
->
setnx
(
foo
=>
'
bar
'
),
'
setnx foo => bar fails
'
);
cmp_ok
(
$o
->
get
(
'
foo
'
),
'
eq
',
'
bar
',
'
get foo = bar
'
);
ok
(
$o
->
set
(
foo
=>
'
baz
'
),
'
set foo => baz
'
);
cmp_ok
(
$o
->
get
(
'
foo
'
),
'
eq
',
'
baz
',
'
get foo = baz
'
);
ok
(
$o
->
set
(
'
test-undef
'
=>
42
),
'
set test-undef
'
);
ok
(
$o
->
set
(
'
test-undef
'
=>
undef
),
'
set undef
'
);
ok
(
!
defined
$o
->
get
(
'
test-undef
'
),
'
get undef
'
);
ok
(
$o
->
exists
(
'
test-undef
'
),
'
exists undef
'
);
$o
->
del
('
non-existant
');
ok
(
!
$o
->
exists
(
'
non-existant
'
),
'
exists non-existant
'
);
ok
(
!
$o
->
get
(
'
non-existant
'
),
'
get non-existant
'
);
ok
(
$o
->
set
('
key-next
'
=>
0
),
'
key-next = 0
'
);
my
$key_next
=
3
;
ok
(
$o
->
set
('
key-left
'
=>
$key_next
),
'
key-left
'
);
is_deeply
(
[
$o
->
mget
(
'
foo
',
'
key-next
',
'
key-left
'
)
],
[
'
baz
',
0
,
3
],
'
mget
'
);
my
@keys
;
foreach
my
$id
(
0
..
$key_next
)
{
my
$key
=
'
key-
'
.
$id
;
push
@keys
,
$key
;
ok
(
$o
->
set
(
$key
=>
$id
),
"
set
$key
"
);
ok
(
$o
->
exists
(
$key
),
"
exists
$key
"
);
cmp_ok
(
$o
->
get
(
$key
),
'
eq
',
$id
,
"
get
$key
"
);
cmp_ok
(
$o
->
incr
(
'
key-next
'
),
'
==
',
$id
+
1
,
'
incr
'
);
cmp_ok
(
$o
->
decr
(
'
key-left
'
),
'
==
',
$key_next
-
$id
-
1
,
'
decr
'
);
}
cmp_ok
(
$o
->
get
(
'
key-next
'
),
'
==
',
$key_next
+
1
,
'
key-next
'
);
ok
(
$o
->
set
('
test-incrby
',
0
),
'
test-incrby
'
);
ok
(
$o
->
set
('
test-decrby
',
0
),
'
test-decry
'
);
foreach
(
1
..
3
)
{
cmp_ok
(
$o
->
incrby
('
test-incrby
',
3
),
'
==
',
$_
*
3
,
'
incrby 3
'
);
cmp_ok
(
$o
->
decrby
('
test-decrby
',
7
),
'
==
',
-
(
$_
*
7
),
'
decrby 7
'
);
}
ok
(
$o
->
del
(
$_
),
"
del
$_
"
)
foreach
map
{
"
key-
$_
"
}
(
'
next
',
'
left
'
);
ok
(
!
$o
->
del
('
non-existing
'
),
'
del non-existing
'
);
cmp_ok
(
$o
->
type
('
foo
'),
'
eq
',
'
string
',
'
type
'
);
cmp_ok
(
$o
->
keys
('
key-*
'),
'
==
',
$key_next
+
1
,
'
key-*
'
);
is_deeply
(
[
$o
->
keys
('
key-*
')
],
[
@keys
],
'
keys
'
);
ok
(
my
$key
=
$o
->
randomkey
,
'
randomkey
'
);
ok
(
$o
->
rename
(
'
test-incrby
',
'
test-renamed
'
),
'
rename
'
);
ok
(
$o
->
exists
(
'
test-renamed
'
),
'
exists test-renamed
'
);
eval
{
$o
->
rename
(
'
test-decrby
',
'
test-renamed
',
1
)
};
ok
(
$@
,
'
rename to existing key
'
);
ok
(
my
$nr_keys
=
$o
->
dbsize
,
'
dbsize
'
);
diag
"
Commands operating on lists
";
my
$list
=
'
test-list
';
$o
->
del
(
$list
)
&&
diag
"
cleanup
$list
from last run
";
ok
(
$o
->
rpush
(
$list
=>
"
r
$_
"
),
'
rpush
'
)
foreach
(
1
..
3
);
ok
(
$o
->
lpush
(
$list
=>
"
l
$_
"
),
'
lpush
'
)
foreach
(
1
..
2
);
cmp_ok
(
$o
->
type
(
$list
),
'
eq
',
'
list
',
'
type
'
);
cmp_ok
(
$o
->
llen
(
$list
),
'
==
',
5
,
'
llen
'
);
is_deeply
(
[
$o
->
lrange
(
$list
,
0
,
1
)
],
[
'
l2
',
'
l1
'
],
'
lrange
'
);
ok
(
$o
->
ltrim
(
$list
,
1
,
2
),
'
ltrim
'
);
cmp_ok
(
$o
->
llen
(
$list
),
'
==
',
2
,
'
llen after ltrim
'
);
cmp_ok
(
$o
->
lindex
(
$list
,
0
),
'
eq
',
'
l1
',
'
lindex
'
);
cmp_ok
(
$o
->
lindex
(
$list
,
1
),
'
eq
',
'
r1
',
'
lindex
'
);
ok
(
$o
->
lset
(
$list
,
0
,
'
foo
'
),
'
lset
'
);
cmp_ok
(
$o
->
lindex
(
$list
,
0
),
'
eq
',
'
foo
',
'
verified
'
);
ok
(
$o
->
lrem
(
$list
,
1
,
'
foo
'
),
'
lrem
'
);
cmp_ok
(
$o
->
llen
(
$list
),
'
==
',
1
,
'
llen after lrem
'
);
cmp_ok
(
$o
->
lpop
(
$list
),
'
eq
',
'
r1
',
'
lpop
'
);
ok
(
!
$o
->
rpop
(
$list
),
'
rpop
'
);
diag
"
Commands operating on sets
";
my
$set
=
'
test-set
';
$o
->
del
(
$set
);
ok
(
$o
->
sadd
(
$set
,
'
foo
'
),
'
sadd
'
);
ok
(
!
$o
->
sadd
(
$set
,
'
foo
'
),
'
sadd
'
);
cmp_ok
(
$o
->
scard
(
$set
),
'
==
',
1
,
'
scard
'
);
ok
(
$o
->
sismember
(
$set
,
'
foo
'
),
'
sismember
'
);
cmp_ok
(
$o
->
type
(
$set
),
'
eq
',
'
set
',
'
type is set
'
);
ok
(
$o
->
srem
(
$set
,
'
foo
'
),
'
srem
'
);
ok
(
!
$o
->
srem
(
$set
,
'
foo
'
),
'
srem again
'
);
cmp_ok
(
$o
->
scard
(
$set
),
'
==
',
0
,
'
scard
'
);
$o
->
sadd
(
'
test-set1
',
$_
)
foreach
(
'
foo
',
'
bar
',
'
baz
'
);
$o
->
sadd
(
'
test-set2
',
$_
)
foreach
(
'
foo
',
'
baz
',
'
xxx
'
);
my
$inter
=
[
'
baz
',
'
foo
'
];
is_deeply
(
[
$o
->
sinter
(
'
test-set1
',
'
test-set2
'
)
],
$inter
,
'
siter
'
);
ok
(
$o
->
sinterstore
(
'
test-set-inter
',
'
test-set1
',
'
test-set2
'
),
'
sinterstore
'
);
cmp_ok
(
$o
->
scard
(
'
test-set-inter
'
),
'
==
',
$#$inter
+
1
,
'
cardinality of intersection
'
);
diag
"
Multiple databases handling commands
";
ok
(
$o
->
select
(
1
),
'
select
'
);
ok
(
$o
->
select
(
0
),
'
select
'
);
ok
(
$o
->
move
(
'
foo
',
1
),
'
move
'
);
ok
(
!
$o
->
exists
(
'
foo
'
),
'
gone
'
);
ok
(
$o
->
select
(
1
),
'
select
'
);
ok
(
$o
->
exists
(
'
foo
'
),
'
exists
'
);
ok
(
$o
->
flushdb
,
'
flushdb
'
);
cmp_ok
(
$o
->
dbsize
,
'
==
',
0
,
'
empty
'
);
diag
"
Sorting
";
ok
(
$o
->
lpush
(
'
test-sort
',
$_
),
"
put
$_
"
)
foreach
(
1
..
4
);
cmp_ok
(
$o
->
llen
(
'
test-sort
'
),
'
==
',
4
,
'
llen
'
);
is_deeply
(
[
$o
->
sort
(
'
test-sort
'
)
],
[
1
,
2
,
3
,
4
],
'
sort
'
);
is_deeply
(
[
$o
->
sort
(
'
test-sort DESC
'
)
],
[
4
,
3
,
2
,
1
],
'
sort DESC
'
);
diag
"
Persistence control commands
";
ok
(
$o
->
save
,
'
save
'
);
ok
(
$o
->
bgsave
,
'
bgsave
'
);
ok
(
$o
->
lastsave
,
'
lastsave
'
);
#ok( $o->shutdown, 'shutdown' );
diag
"
shutdown not tested
";
diag
"
Remote server control commands
";
ok
(
my
$info
=
$o
->
info
,
'
info
'
);
diag
dump
(
$info
);
diag
"
Connection handling
";
ok
(
$o
->
quit
,
'
quit
'
);
client-libraries/perl/t/10-Redis-List.t
0 → 100755
View file @
f78fd11b
#!/usr/bin/perl
use
warnings
;
use
strict
;
use
Test::
More
tests
=>
8
;
use
lib
'
lib
';
use
Data::
Dump
qw/dump/
;
BEGIN
{
use_ok
(
'
Redis::List
'
);
}
my
@a
;
ok
(
my
$o
=
tie
(
@a
,
'
Redis::List
',
'
test-redis-list
'
),
'
tie
'
);
isa_ok
(
$o
,
'
Redis::List
'
);
$o
->
CLEAR
;
ok
(
!
@a
,
'
empty list
'
);
ok
(
@a
=
(
'
foo
',
'
bar
',
'
baz
'
),
'
=
'
);
is_deeply
(
[
@a
],
[
'
foo
',
'
bar
',
'
baz
'
]
);
ok
(
push
(
@a
,
'
push
'
),
'
push
'
);
is_deeply
(
[
@a
],
[
'
foo
',
'
bar
',
'
baz
',
'
push
'
]
);
#diag dump( @a );
client-libraries/perl/t/20-Redis-Hash.t
0 → 100755
View file @
f78fd11b
#!/usr/bin/perl
use
warnings
;
use
strict
;
use
Test::
More
tests
=>
7
;
use
lib
'
lib
';
use
Data::
Dump
qw/dump/
;
BEGIN
{
use_ok
(
'
Redis::Hash
'
);
}
ok
(
my
$o
=
tie
(
my
%h
,
'
Redis::Hash
',
'
test-redis-hash
'
),
'
tie
'
);
isa_ok
(
$o
,
'
Redis::Hash
'
);
$o
->
CLEAR
();
ok
(
!
keys
%h
,
'
empty
'
);
ok
(
%h
=
(
'
foo
'
=>
42
,
'
bar
'
=>
1
,
'
baz
'
=>
99
),
'
=
'
);
is_deeply
(
[
sort
keys
%h
],
[
'
bar
',
'
baz
',
'
foo
'
],
'
keys
'
);
is_deeply
(
\
%h
,
{
bar
=>
1
,
baz
=>
99
,
foo
=>
42
,
},
'
structure
'
);
#diag dump( \%h );
client-libraries/perl/t/pod-coverage.t
0 → 100644
View file @
f78fd11b
use
strict
;
use
warnings
;
use
Test::
More
;
# Ensure a recent version of Test::Pod::Coverage
my
$min_tpc
=
1.08
;
eval
"
use Test::Pod::Coverage
$min_tpc
";
plan
skip_all
=>
"
Test::Pod::Coverage
$min_tpc
required for testing POD coverage
"
if
$@
;
# Test::Pod::Coverage doesn't require a minimum Pod::Coverage version,
# but older versions don't recognize some common documentation styles
my
$min_pc
=
0.18
;
eval
"
use Pod::Coverage
$min_pc
";
plan
skip_all
=>
"
Pod::Coverage
$min_pc
required for testing POD coverage
"
if
$@
;
all_pod_coverage_ok
();
client-libraries/perl/t/pod.t
0 → 100644
View file @
f78fd11b
#!perl -T
use
strict
;
use
warnings
;
use
Test::
More
;
# Ensure a recent version of Test::Pod
my
$min_tp
=
1.22
;
eval
"
use Test::Pod
$min_tp
";
plan
skip_all
=>
"
Test::Pod
$min_tp
required for testing POD
"
if
$@
;
all_pod_files_ok
();
redis.c
View file @
f78fd11b
...
@@ -46,6 +46,7 @@
...
@@ -46,6 +46,7 @@
#include <fcntl.h>
#include <fcntl.h>
#include <sys/time.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/resource.h>
#include <limits.h>
#include "ae.h"
/* Event driven programming library */
#include "ae.h"
/* Event driven programming library */
#include "sds.h"
/* Dynamic safe strings */
#include "sds.h"
/* Dynamic safe strings */
...
@@ -82,9 +83,28 @@
...
@@ -82,9 +83,28 @@
#define REDIS_LIST 1
#define REDIS_LIST 1
#define REDIS_SET 2
#define REDIS_SET 2
#define REDIS_HASH 3
#define REDIS_HASH 3
/* Object types only used for dumping to disk */
#define REDIS_SELECTDB 254
#define REDIS_SELECTDB 254
#define REDIS_EOF 255
#define REDIS_EOF 255
/* Defines related to the dump file format. To store 32 bits lengths for short
* keys requires a lot of space, so we check the most significant 2 bits of
* the first byte to interpreter the length:
*
* 00|000000 => if the two MSB are 00 the len is the 6 bits of this byte
* 01|000000 00000000 => 01, the len is 14 byes, 6 bits + 8 bits of next byte
* 10|000000 [32 bit integer] => if it's 01, a full 32 bit len will follow
* 11|000000 [64 bit integer] => if it's 11, a full 64 bit len will follow
*
* 64 bit lengths are not used currently. Lenghts up to 63 are stored using
* a single byte, most DB keys, and may values, will fit inside. */
#define REDIS_RDB_6BITLEN 0
#define REDIS_RDB_14BITLEN 1
#define REDIS_RDB_32BITLEN 2
#define REDIS_RDB_64BITLEN 3
#define REDIS_RDB_LENERR UINT_MAX
/* Client flags */
/* Client flags */
#define REDIS_CLOSE 1
/* This client connection should be closed ASAP */
#define REDIS_CLOSE 1
/* This client connection should be closed ASAP */
#define REDIS_SLAVE 2
/* This client is a slave server */
#define REDIS_SLAVE 2
/* This client is a slave server */
...
@@ -230,11 +250,11 @@ static void freeSetObject(robj *o);
...
@@ -230,11 +250,11 @@ static void freeSetObject(robj *o);
static
void
decrRefCount
(
void
*
o
);
static
void
decrRefCount
(
void
*
o
);
static
robj
*
createObject
(
int
type
,
void
*
ptr
);
static
robj
*
createObject
(
int
type
,
void
*
ptr
);
static
void
freeClient
(
redisClient
*
c
);
static
void
freeClient
(
redisClient
*
c
);
static
int
l
oad
Db
(
char
*
filename
);
static
int
rdbL
oad
(
char
*
filename
);
static
void
addReply
(
redisClient
*
c
,
robj
*
obj
);
static
void
addReply
(
redisClient
*
c
,
robj
*
obj
);
static
void
addReplySds
(
redisClient
*
c
,
sds
s
);
static
void
addReplySds
(
redisClient
*
c
,
sds
s
);
static
void
incrRefCount
(
robj
*
o
);
static
void
incrRefCount
(
robj
*
o
);
static
int
s
ave
Db
Background
(
char
*
filename
);
static
int
rdbS
aveBackground
(
char
*
filename
);
static
robj
*
createStringObject
(
char
*
ptr
,
size_t
len
);
static
robj
*
createStringObject
(
char
*
ptr
,
size_t
len
);
static
void
replicationFeedSlaves
(
list
*
slaves
,
struct
redisCommand
*
cmd
,
int
dictid
,
robj
**
argv
,
int
argc
);
static
void
replicationFeedSlaves
(
list
*
slaves
,
struct
redisCommand
*
cmd
,
int
dictid
,
robj
**
argv
,
int
argc
);
static
int
syncWithMaster
(
void
);
static
int
syncWithMaster
(
void
);
...
@@ -641,7 +661,7 @@ int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
...
@@ -641,7 +661,7 @@ int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
now
-
server
.
lastsave
>
sp
->
seconds
)
{
now
-
server
.
lastsave
>
sp
->
seconds
)
{
redisLog
(
REDIS_NOTICE
,
"%d changes in %d seconds. Saving..."
,
redisLog
(
REDIS_NOTICE
,
"%d changes in %d seconds. Saving..."
,
sp
->
changes
,
sp
->
seconds
);
sp
->
changes
,
sp
->
seconds
);
s
ave
Db
Background
(
server
.
dbfilename
);
rdbS
aveBackground
(
server
.
dbfilename
);
break
;
break
;
}
}
}
}
...
@@ -1394,12 +1414,37 @@ static void decrRefCount(void *obj) {
...
@@ -1394,12 +1414,37 @@ static void decrRefCount(void *obj) {
/*============================ DB saving/loading ============================ */
/*============================ DB saving/loading ============================ */
static
int
rdbSaveType
(
FILE
*
fp
,
unsigned
char
type
)
{
if
(
fwrite
(
&
type
,
1
,
1
,
fp
)
==
0
)
return
-
1
;
return
0
;
}
static
int
rdbSaveLen
(
FILE
*
fp
,
uint32_t
len
)
{
unsigned
char
buf
[
2
];
if
(
len
<
(
1
<<
6
))
{
/* Save a 6 bit len */
buf
[
0
]
=
(
len
&
0xFF
)
|
REDIS_RDB_6BITLEN
;
if
(
fwrite
(
buf
,
1
,
1
,
fp
)
==
0
)
return
-
1
;
}
else
if
(
len
<
(
1
<<
14
))
{
/* Save a 14 bit len */
buf
[
0
]
=
((
len
>>
8
)
&
0xFF
)
|
REDIS_RDB_14BITLEN
;
buf
[
1
]
=
len
&
0xFF
;
if
(
fwrite
(
buf
,
4
,
1
,
fp
)
==
0
)
return
-
1
;
}
else
{
/* Save a 32 bit len */
buf
[
0
]
=
REDIS_RDB_32BITLEN
;
if
(
fwrite
(
buf
,
1
,
1
,
fp
)
==
0
)
return
-
1
;
len
=
htonl
(
len
);
if
(
fwrite
(
&
len
,
4
,
1
,
fp
)
==
0
)
return
-
1
;
}
return
0
;
}
/* Save the DB on disk. Return REDIS_ERR on error, REDIS_OK on success */
/* Save the DB on disk. Return REDIS_ERR on error, REDIS_OK on success */
static
int
s
ave
Db
(
char
*
filename
)
{
static
int
rdbS
ave
(
char
*
filename
)
{
dictIterator
*
di
=
NULL
;
dictIterator
*
di
=
NULL
;
dictEntry
*
de
;
dictEntry
*
de
;
uint32_t
len
;
uint8_t
type
;
FILE
*
fp
;
FILE
*
fp
;
char
tmpfile
[
256
];
char
tmpfile
[
256
];
int
j
;
int
j
;
...
@@ -1410,7 +1455,7 @@ static int saveDb(char *filename) {
...
@@ -1410,7 +1455,7 @@ static int saveDb(char *filename) {
redisLog
(
REDIS_WARNING
,
"Failed saving the DB: %s"
,
strerror
(
errno
));
redisLog
(
REDIS_WARNING
,
"Failed saving the DB: %s"
,
strerror
(
errno
));
return
REDIS_ERR
;
return
REDIS_ERR
;
}
}
if
(
fwrite
(
"REDIS000
0
"
,
9
,
1
,
fp
)
==
0
)
goto
werr
;
if
(
fwrite
(
"REDIS000
1
"
,
9
,
1
,
fp
)
==
0
)
goto
werr
;
for
(
j
=
0
;
j
<
server
.
dbnum
;
j
++
)
{
for
(
j
=
0
;
j
<
server
.
dbnum
;
j
++
)
{
dict
*
d
=
server
.
dict
[
j
];
dict
*
d
=
server
.
dict
[
j
];
if
(
dictGetHashTableUsed
(
d
)
==
0
)
continue
;
if
(
dictGetHashTableUsed
(
d
)
==
0
)
continue
;
...
@@ -1421,59 +1466,54 @@ static int saveDb(char *filename) {
...
@@ -1421,59 +1466,54 @@ static int saveDb(char *filename) {
}
}
/* Write the SELECT DB opcode */
/* Write the SELECT DB opcode */
type
=
REDIS_SELECTDB
;
if
(
rdbSaveType
(
fp
,
REDIS_SELECTDB
)
==
-
1
)
goto
werr
;
len
=
htonl
(
j
);
if
(
rdbSaveLen
(
fp
,
j
)
==
-
1
)
goto
werr
;
if
(
fwrite
(
&
type
,
1
,
1
,
fp
)
==
0
)
goto
werr
;
if
(
fwrite
(
&
len
,
4
,
1
,
fp
)
==
0
)
goto
werr
;
/* Iterate this DB writing every entry */
/* Iterate this DB writing every entry */
while
((
de
=
dictNext
(
di
))
!=
NULL
)
{
while
((
de
=
dictNext
(
di
))
!=
NULL
)
{
robj
*
key
=
dictGetEntryKey
(
de
);
robj
*
key
=
dictGetEntryKey
(
de
);
robj
*
o
=
dictGetEntryVal
(
de
);
robj
*
o
=
dictGetEntryVal
(
de
);
type
=
o
->
type
;
if
(
rdbSaveType
(
fp
,
o
->
type
)
==
-
1
)
goto
werr
;
len
=
htonl
(
sdslen
(
key
->
ptr
));
if
(
rdbSaveLen
(
fp
,
sdslen
(
key
->
ptr
))
==
-
1
)
goto
werr
;
if
(
fwrite
(
&
type
,
1
,
1
,
fp
)
==
0
)
goto
werr
;
if
(
fwrite
(
&
len
,
4
,
1
,
fp
)
==
0
)
goto
werr
;
if
(
fwrite
(
key
->
ptr
,
sdslen
(
key
->
ptr
),
1
,
fp
)
==
0
)
goto
werr
;
if
(
fwrite
(
key
->
ptr
,
sdslen
(
key
->
ptr
),
1
,
fp
)
==
0
)
goto
werr
;
if
(
type
==
REDIS_STRING
)
{
if
(
o
->
type
==
REDIS_STRING
)
{
/* Save a string value */
/* Save a string value */
sds
sval
=
o
->
ptr
;
sds
sval
=
o
->
ptr
;
len
=
htonl
(
sdslen
(
sval
));
if
(
fwrite
(
&
len
,
4
,
1
,
fp
)
==
0
)
goto
werr
;
if
(
rdbSaveLen
(
fp
,
sdslen
(
sval
)
)
==
-
1
)
goto
werr
;
if
(
sdslen
(
sval
)
&&
if
(
sdslen
(
sval
)
&&
fwrite
(
sval
,
sdslen
(
sval
),
1
,
fp
)
==
0
)
goto
werr
;
fwrite
(
sval
,
sdslen
(
sval
),
1
,
fp
)
==
0
)
goto
werr
;
}
else
if
(
type
==
REDIS_LIST
)
{
}
else
if
(
o
->
type
==
REDIS_LIST
)
{
/* Save a list value */
/* Save a list value */
list
*
list
=
o
->
ptr
;
list
*
list
=
o
->
ptr
;
listNode
*
ln
=
list
->
head
;
listNode
*
ln
=
list
->
head
;
len
=
htonl
(
listLength
(
list
));
if
(
rdbSaveLen
(
fp
,
listLength
(
list
))
==
-
1
)
goto
werr
;
if
(
fwrite
(
&
len
,
4
,
1
,
fp
)
==
0
)
goto
werr
;
while
(
ln
)
{
while
(
ln
)
{
robj
*
eleobj
=
listNodeValue
(
ln
);
robj
*
eleobj
=
listNodeValue
(
ln
);
len
=
htonl
(
sdslen
(
eleobj
->
ptr
));
if
(
fwrite
(
&
len
,
4
,
1
,
fp
)
==
0
)
goto
werr
;
if
(
rdbSaveLen
(
fp
,
sdslen
(
eleobj
->
ptr
))
==
-
1
)
goto
werr
;
if
(
sdslen
(
eleobj
->
ptr
)
&&
fwrite
(
eleobj
->
ptr
,
sdslen
(
eleobj
->
ptr
),
1
,
fp
)
==
0
)
if
(
sdslen
(
eleobj
->
ptr
)
&&
fwrite
(
eleobj
->
ptr
,
sdslen
(
eleobj
->
ptr
),
1
,
fp
)
==
0
)
goto
werr
;
goto
werr
;
ln
=
ln
->
next
;
ln
=
ln
->
next
;
}
}
}
else
if
(
type
==
REDIS_SET
)
{
}
else
if
(
o
->
type
==
REDIS_SET
)
{
/* Save a set value */
/* Save a set value */
dict
*
set
=
o
->
ptr
;
dict
*
set
=
o
->
ptr
;
dictIterator
*
di
=
dictGetIterator
(
set
);
dictIterator
*
di
=
dictGetIterator
(
set
);
dictEntry
*
de
;
dictEntry
*
de
;
if
(
!
set
)
oom
(
"dictGetIteraotr"
);
if
(
!
set
)
oom
(
"dictGetIteraotr"
);
len
=
htonl
(
dictGetHashTableUsed
(
set
));
if
(
rdbSaveLen
(
fp
,
dictGetHashTableUsed
(
set
))
==
-
1
)
goto
werr
;
if
(
fwrite
(
&
len
,
4
,
1
,
fp
)
==
0
)
goto
werr
;
while
((
de
=
dictNext
(
di
))
!=
NULL
)
{
while
((
de
=
dictNext
(
di
))
!=
NULL
)
{
robj
*
eleobj
;
robj
*
eleobj
;
eleobj
=
dictGetEntryKey
(
de
);
eleobj
=
dictGetEntryKey
(
de
);
len
=
htonl
(
sdslen
(
eleobj
->
ptr
));
if
(
rdbSaveLen
(
fp
,
sdslen
(
eleobj
->
ptr
))
==
-
1
)
goto
werr
;
if
(
fwrite
(
&
len
,
4
,
1
,
fp
)
==
0
)
goto
werr
;
if
(
sdslen
(
eleobj
->
ptr
)
&&
if
(
sdslen
(
eleobj
->
ptr
)
&&
fwrite
(
eleobj
->
ptr
,
sdslen
(
eleobj
->
ptr
),
1
,
fp
)
==
0
)
fwrite
(
eleobj
->
ptr
,
sdslen
(
eleobj
->
ptr
),
1
,
fp
)
==
0
)
goto
werr
;
goto
werr
;
}
}
dictReleaseIterator
(
di
);
dictReleaseIterator
(
di
);
...
@@ -1484,8 +1524,9 @@ static int saveDb(char *filename) {
...
@@ -1484,8 +1524,9 @@ static int saveDb(char *filename) {
dictReleaseIterator
(
di
);
dictReleaseIterator
(
di
);
}
}
/* EOF opcode */
/* EOF opcode */
type
=
REDIS_EOF
;
if
(
rdbSaveType
(
fp
,
REDIS_EOF
)
==
-
1
)
goto
werr
;
if
(
fwrite
(
&
type
,
1
,
1
,
fp
)
==
0
)
goto
werr
;
/* Make sure data will not remain on the OS's output buffers */
fflush
(
fp
);
fflush
(
fp
);
fsync
(
fileno
(
fp
));
fsync
(
fileno
(
fp
));
fclose
(
fp
);
fclose
(
fp
);
...
@@ -1510,14 +1551,14 @@ werr:
...
@@ -1510,14 +1551,14 @@ werr:
return
REDIS_ERR
;
return
REDIS_ERR
;
}
}
static
int
s
ave
Db
Background
(
char
*
filename
)
{
static
int
rdbS
aveBackground
(
char
*
filename
)
{
pid_t
childpid
;
pid_t
childpid
;
if
(
server
.
bgsaveinprogress
)
return
REDIS_ERR
;
if
(
server
.
bgsaveinprogress
)
return
REDIS_ERR
;
if
((
childpid
=
fork
())
==
0
)
{
if
((
childpid
=
fork
())
==
0
)
{
/* Child */
/* Child */
close
(
server
.
fd
);
close
(
server
.
fd
);
if
(
s
ave
Db
(
filename
)
==
REDIS_OK
)
{
if
(
rdbS
ave
(
filename
)
==
REDIS_OK
)
{
exit
(
0
);
exit
(
0
);
}
else
{
}
else
{
exit
(
1
);
exit
(
1
);
...
@@ -1531,90 +1572,109 @@ static int saveDbBackground(char *filename) {
...
@@ -1531,90 +1572,109 @@ static int saveDbBackground(char *filename) {
return
REDIS_OK
;
/* unreached */
return
REDIS_OK
;
/* unreached */
}
}
static
int
l
oadType
(
FILE
*
fp
)
{
static
int
rdbL
oadType
(
FILE
*
fp
)
{
u
int8_t
type
;
u
nsigned
char
type
;
if
(
fread
(
&
type
,
1
,
1
,
fp
)
==
0
)
return
-
1
;
if
(
fread
(
&
type
,
1
,
1
,
fp
)
==
0
)
return
-
1
;
return
type
;
return
type
;
}
}
static
int
loadDb
(
char
*
filename
)
{
static
uint32_t
rdbLoadLen
(
FILE
*
fp
,
int
rdbver
)
{
unsigned
char
buf
[
2
];
uint32_t
len
;
if
(
rdbver
==
0
)
{
if
(
fread
(
&
len
,
4
,
1
,
fp
)
==
0
)
return
REDIS_RDB_LENERR
;
return
ntohl
(
len
);
}
else
{
if
(
fread
(
buf
,
1
,
1
,
fp
)
==
0
)
return
REDIS_RDB_LENERR
;
if
((
buf
[
0
]
&
0xC0
)
==
REDIS_RDB_6BITLEN
)
{
/* Read a 6 bit len */
return
buf
[
0
];
}
else
if
((
buf
[
0
]
&
0xC0
)
==
REDIS_RDB_14BITLEN
)
{
/* Read a 14 bit len */
if
(
fread
(
buf
+
1
,
1
,
1
,
fp
)
==
0
)
return
REDIS_RDB_LENERR
;
return
((
buf
[
0
]
&
0x3F
)
<<
8
)
|
buf
[
1
];
}
else
{
/* Read a 32 bit len */
if
(
fread
(
&
len
,
4
,
1
,
fp
)
==
0
)
return
REDIS_RDB_LENERR
;
return
ntohl
(
len
);
}
}
return
0
;
}
static
robj
*
rdbLoadStringObject
(
FILE
*
fp
,
int
rdbver
)
{
uint32_t
len
=
rdbLoadLen
(
fp
,
rdbver
);
sds
val
;
if
(
len
==
REDIS_RDB_LENERR
)
return
NULL
;
val
=
sdsnewlen
(
NULL
,
len
);
if
(
len
&&
fread
(
val
,
len
,
1
,
fp
)
==
0
)
{
sdsfree
(
val
);
return
NULL
;
}
return
createObject
(
REDIS_STRING
,
val
);
}
static
int
rdbLoad
(
char
*
filename
)
{
FILE
*
fp
;
FILE
*
fp
;
char
buf
[
REDIS_LOADBUF_LEN
];
/* Try to use this buffer instead of */
robj
*
keyobj
=
NULL
;
char
vbuf
[
REDIS_LOADBUF_LEN
];
/* malloc() when the element is small */
uint32_t
dbid
;
char
*
key
=
NULL
,
*
val
=
NULL
;
uint32_t
klen
,
vlen
,
dbid
;
int
type
;
int
type
;
int
retval
;
int
retval
;
dict
*
d
=
server
.
dict
[
0
];
dict
*
d
=
server
.
dict
[
0
];
char
buf
[
1024
];
int
rdbver
;
fp
=
fopen
(
filename
,
"r"
);
fp
=
fopen
(
filename
,
"r"
);
if
(
!
fp
)
return
REDIS_ERR
;
if
(
!
fp
)
return
REDIS_ERR
;
if
(
fread
(
buf
,
9
,
1
,
fp
)
==
0
)
goto
eoferr
;
if
(
fread
(
buf
,
9
,
1
,
fp
)
==
0
)
goto
eoferr
;
if
(
memcmp
(
buf
,
"REDIS0000"
,
9
)
!=
0
)
{
buf
[
9
]
=
'\0'
;
if
(
memcmp
(
buf
,
"REDIS"
,
5
)
!=
0
)
{
fclose
(
fp
);
fclose
(
fp
);
redisLog
(
REDIS_WARNING
,
"Wrong signature trying to load DB from file"
);
redisLog
(
REDIS_WARNING
,
"Wrong signature trying to load DB from file"
);
return
REDIS_ERR
;
return
REDIS_ERR
;
}
}
rdbver
=
atoi
(
buf
+
5
);
if
(
rdbver
>
1
)
{
fclose
(
fp
);
redisLog
(
REDIS_WARNING
,
"Can't handle RDB format version %d"
,
rdbver
);
return
REDIS_ERR
;
}
while
(
1
)
{
while
(
1
)
{
robj
*
o
;
robj
*
o
;
/* Read type. */
/* Read type. */
if
((
type
=
l
oadType
(
fp
))
==
-
1
)
goto
eoferr
;
if
((
type
=
rdbL
oadType
(
fp
))
==
-
1
)
goto
eoferr
;
if
(
type
==
REDIS_EOF
)
break
;
if
(
type
==
REDIS_EOF
)
break
;
/* Handle SELECT DB opcode as a special case */
/* Handle SELECT DB opcode as a special case */
if
(
type
==
REDIS_SELECTDB
)
{
if
(
type
==
REDIS_SELECTDB
)
{
if
(
fread
(
&
dbid
,
4
,
1
,
fp
)
==
0
)
goto
eoferr
;
if
((
dbid
=
rdbLoadLen
(
fp
,
rdbver
))
==
REDIS_RDB_LENERR
)
goto
eoferr
;
dbid
=
ntohl
(
dbid
);
if
(
dbid
>=
(
unsigned
)
server
.
dbnum
)
{
if
(
dbid
>=
(
unsigned
)
server
.
dbnum
)
{
redisLog
(
REDIS_WARNING
,
"FATAL: Data file was created with a Redis server co
mpil
ed to handle more than %d databases. Exiting
\n
"
,
server
.
dbnum
);
redisLog
(
REDIS_WARNING
,
"FATAL: Data file was created with a Redis server co
nfigur
ed to handle more than %d databases. Exiting
\n
"
,
server
.
dbnum
);
exit
(
1
);
exit
(
1
);
}
}
d
=
server
.
dict
[
dbid
];
d
=
server
.
dict
[
dbid
];
continue
;
continue
;
}
}
/* Read key */
/* Read key */
if
(
fread
(
&
klen
,
4
,
1
,
fp
)
==
0
)
goto
eoferr
;
if
((
keyobj
=
rdbLoadStringObject
(
fp
,
rdbver
))
==
NULL
)
goto
eoferr
;
klen
=
ntohl
(
klen
);
if
(
klen
<=
REDIS_LOADBUF_LEN
)
{
key
=
buf
;
}
else
{
key
=
zmalloc
(
klen
);
if
(
!
key
)
oom
(
"Loading DB from file"
);
}
if
(
fread
(
key
,
klen
,
1
,
fp
)
==
0
)
goto
eoferr
;
if
(
type
==
REDIS_STRING
)
{
if
(
type
==
REDIS_STRING
)
{
/* Read string value */
/* Read string value */
if
(
fread
(
&
vlen
,
4
,
1
,
fp
)
==
0
)
goto
eoferr
;
if
((
o
=
rdbLoadStringObject
(
fp
,
rdbver
))
==
NULL
)
goto
eoferr
;
vlen
=
ntohl
(
vlen
);
if
(
vlen
<=
REDIS_LOADBUF_LEN
)
{
val
=
vbuf
;
}
else
{
val
=
zmalloc
(
vlen
);
if
(
!
val
)
oom
(
"Loading DB from file"
);
}
if
(
vlen
&&
fread
(
val
,
vlen
,
1
,
fp
)
==
0
)
goto
eoferr
;
o
=
createObject
(
REDIS_STRING
,
sdsnewlen
(
val
,
vlen
));
}
else
if
(
type
==
REDIS_LIST
||
type
==
REDIS_SET
)
{
}
else
if
(
type
==
REDIS_LIST
||
type
==
REDIS_SET
)
{
/* Read list/set value */
/* Read list/set value */
uint32_t
listlen
;
uint32_t
listlen
;
if
(
fread
(
&
listlen
,
4
,
1
,
fp
)
==
0
)
goto
eoferr
;
listlen
=
ntohl
(
listlen
);
if
((
listlen
=
rdbLoadLen
(
fp
,
rdbver
))
==
REDIS_RDB_LENERR
)
goto
eoferr
;
o
=
(
type
==
REDIS_LIST
)
?
createListObject
()
:
createSetObject
();
o
=
(
type
==
REDIS_LIST
)
?
createListObject
()
:
createSetObject
();
/* Load every single element of the list/set */
/* Load every single element of the list/set */
while
(
listlen
--
)
{
while
(
listlen
--
)
{
robj
*
ele
;
robj
*
ele
;
if
(
fread
(
&
vlen
,
4
,
1
,
fp
)
==
0
)
goto
eoferr
;
if
((
ele
=
rdbLoadStringObject
(
fp
,
rdbver
))
==
NULL
)
goto
eoferr
;
vlen
=
ntohl
(
vlen
);
if
(
vlen
<=
REDIS_LOADBUF_LEN
)
{
val
=
vbuf
;
}
else
{
val
=
zmalloc
(
vlen
);
if
(
!
val
)
oom
(
"Loading DB from file"
);
}
if
(
vlen
&&
fread
(
val
,
vlen
,
1
,
fp
)
==
0
)
goto
eoferr
;
ele
=
createObject
(
REDIS_STRING
,
sdsnewlen
(
val
,
vlen
));
if
(
type
==
REDIS_LIST
)
{
if
(
type
==
REDIS_LIST
)
{
if
(
!
listAddNodeTail
((
list
*
)
o
->
ptr
,
ele
))
if
(
!
listAddNodeTail
((
list
*
)
o
->
ptr
,
ele
))
oom
(
"listAddNodeTail"
);
oom
(
"listAddNodeTail"
);
...
@@ -1622,30 +1682,23 @@ static int loadDb(char *filename) {
...
@@ -1622,30 +1682,23 @@ static int loadDb(char *filename) {
if
(
dictAdd
((
dict
*
)
o
->
ptr
,
ele
,
NULL
)
==
DICT_ERR
)
if
(
dictAdd
((
dict
*
)
o
->
ptr
,
ele
,
NULL
)
==
DICT_ERR
)
oom
(
"dictAdd"
);
oom
(
"dictAdd"
);
}
}
/* free the temp buffer if needed */
if
(
val
!=
vbuf
)
zfree
(
val
);
val
=
NULL
;
}
}
}
else
{
}
else
{
assert
(
0
!=
0
);
assert
(
0
!=
0
);
}
}
/* Add the new object in the hash table */
/* Add the new object in the hash table */
retval
=
dictAdd
(
d
,
createStringObject
(
key
,
klen
)
,
o
);
retval
=
dictAdd
(
d
,
keyobj
,
o
);
if
(
retval
==
DICT_ERR
)
{
if
(
retval
==
DICT_ERR
)
{
redisLog
(
REDIS_WARNING
,
"Loading DB, duplicated key found! Unrecoverable error, exiting now."
);
redisLog
(
REDIS_WARNING
,
"Loading DB, duplicated key
(%s)
found! Unrecoverable error, exiting now."
,
keyobj
->
ptr
);
exit
(
1
);
exit
(
1
);
}
}
/* Iteration cleanup */
keyobj
=
o
=
NULL
;
if
(
key
!=
buf
)
zfree
(
key
);
if
(
val
!=
vbuf
)
zfree
(
val
);
key
=
val
=
NULL
;
}
}
fclose
(
fp
);
fclose
(
fp
);
return
REDIS_OK
;
return
REDIS_OK
;
eoferr:
/* unexpected end of file is handled here with a fatal exit */
eoferr:
/* unexpected end of file is handled here with a fatal exit */
if
(
key
!=
buf
)
zfree
(
key
);
decrRefCount
(
keyobj
);
if
(
val
!=
vbuf
)
zfree
(
val
);
redisLog
(
REDIS_WARNING
,
"Short read loading DB. Unrecoverable error, exiting now."
);
redisLog
(
REDIS_WARNING
,
"Short read loading DB. Unrecoverable error, exiting now."
);
exit
(
1
);
exit
(
1
);
return
REDIS_ERR
;
/* Just to avoid warning */
return
REDIS_ERR
;
/* Just to avoid warning */
...
@@ -1894,7 +1947,7 @@ static void typeCommand(redisClient *c) {
...
@@ -1894,7 +1947,7 @@ static void typeCommand(redisClient *c) {
}
}
static
void
saveCommand
(
redisClient
*
c
)
{
static
void
saveCommand
(
redisClient
*
c
)
{
if
(
s
ave
Db
(
server
.
dbfilename
)
==
REDIS_OK
)
{
if
(
rdbS
ave
(
server
.
dbfilename
)
==
REDIS_OK
)
{
addReply
(
c
,
shared
.
ok
);
addReply
(
c
,
shared
.
ok
);
}
else
{
}
else
{
addReply
(
c
,
shared
.
err
);
addReply
(
c
,
shared
.
err
);
...
@@ -1906,7 +1959,7 @@ static void bgsaveCommand(redisClient *c) {
...
@@ -1906,7 +1959,7 @@ static void bgsaveCommand(redisClient *c) {
addReplySds
(
c
,
sdsnew
(
"-ERR background save already in progress
\r\n
"
));
addReplySds
(
c
,
sdsnew
(
"-ERR background save already in progress
\r\n
"
));
return
;
return
;
}
}
if
(
s
ave
Db
Background
(
server
.
dbfilename
)
==
REDIS_OK
)
{
if
(
rdbS
aveBackground
(
server
.
dbfilename
)
==
REDIS_OK
)
{
addReply
(
c
,
shared
.
ok
);
addReply
(
c
,
shared
.
ok
);
}
else
{
}
else
{
addReply
(
c
,
shared
.
err
);
addReply
(
c
,
shared
.
err
);
...
@@ -1915,7 +1968,7 @@ static void bgsaveCommand(redisClient *c) {
...
@@ -1915,7 +1968,7 @@ static void bgsaveCommand(redisClient *c) {
static
void
shutdownCommand
(
redisClient
*
c
)
{
static
void
shutdownCommand
(
redisClient
*
c
)
{
redisLog
(
REDIS_WARNING
,
"User requested shutdown, saving DB..."
);
redisLog
(
REDIS_WARNING
,
"User requested shutdown, saving DB..."
);
if
(
s
ave
Db
(
server
.
dbfilename
)
==
REDIS_OK
)
{
if
(
rdbS
ave
(
server
.
dbfilename
)
==
REDIS_OK
)
{
if
(
server
.
daemonize
)
{
if
(
server
.
daemonize
)
{
unlink
(
server
.
pidfile
);
unlink
(
server
.
pidfile
);
}
}
...
@@ -2508,13 +2561,13 @@ static void sinterstoreCommand(redisClient *c) {
...
@@ -2508,13 +2561,13 @@ static void sinterstoreCommand(redisClient *c) {
static
void
flushdbCommand
(
redisClient
*
c
)
{
static
void
flushdbCommand
(
redisClient
*
c
)
{
dictEmpty
(
c
->
dict
);
dictEmpty
(
c
->
dict
);
addReply
(
c
,
shared
.
ok
);
addReply
(
c
,
shared
.
ok
);
s
ave
Db
(
server
.
dbfilename
);
rdbS
ave
(
server
.
dbfilename
);
}
}
static
void
flushallCommand
(
redisClient
*
c
)
{
static
void
flushallCommand
(
redisClient
*
c
)
{
emptyDb
();
emptyDb
();
addReply
(
c
,
shared
.
ok
);
addReply
(
c
,
shared
.
ok
);
s
ave
Db
(
server
.
dbfilename
);
rdbS
ave
(
server
.
dbfilename
);
}
}
redisSortOperation
*
createSortOperation
(
int
type
,
robj
*
pattern
)
{
redisSortOperation
*
createSortOperation
(
int
type
,
robj
*
pattern
)
{
...
@@ -2923,7 +2976,8 @@ static void syncCommand(redisClient *c) {
...
@@ -2923,7 +2976,8 @@ static void syncCommand(redisClient *c) {
if
(
c
->
flags
&
REDIS_SLAVE
)
return
;
if
(
c
->
flags
&
REDIS_SLAVE
)
return
;
redisLog
(
REDIS_NOTICE
,
"Slave ask for syncronization"
);
redisLog
(
REDIS_NOTICE
,
"Slave ask for syncronization"
);
if
(
flushClientOutput
(
c
)
==
REDIS_ERR
||
saveDb
(
server
.
dbfilename
)
!=
REDIS_OK
)
if
(
flushClientOutput
(
c
)
==
REDIS_ERR
||
rdbSave
(
server
.
dbfilename
)
!=
REDIS_OK
)
goto
closeconn
;
goto
closeconn
;
fd
=
open
(
server
.
dbfilename
,
O_RDONLY
);
fd
=
open
(
server
.
dbfilename
,
O_RDONLY
);
...
@@ -3020,7 +3074,7 @@ static int syncWithMaster(void) {
...
@@ -3020,7 +3074,7 @@ static int syncWithMaster(void) {
return
REDIS_ERR
;
return
REDIS_ERR
;
}
}
emptyDb
();
emptyDb
();
if
(
l
oad
Db
(
server
.
dbfilename
)
!=
REDIS_OK
)
{
if
(
rdbL
oad
(
server
.
dbfilename
)
!=
REDIS_OK
)
{
redisLog
(
REDIS_WARNING
,
"Failed trying to load the MASTER synchronization DB from disk"
);
redisLog
(
REDIS_WARNING
,
"Failed trying to load the MASTER synchronization DB from disk"
);
close
(
fd
);
close
(
fd
);
return
REDIS_ERR
;
return
REDIS_ERR
;
...
@@ -3079,7 +3133,7 @@ int main(int argc, char **argv) {
...
@@ -3079,7 +3133,7 @@ int main(int argc, char **argv) {
initServer
();
initServer
();
if
(
server
.
daemonize
)
daemonize
();
if
(
server
.
daemonize
)
daemonize
();
redisLog
(
REDIS_NOTICE
,
"Server started, Redis version "
REDIS_VERSION
);
redisLog
(
REDIS_NOTICE
,
"Server started, Redis version "
REDIS_VERSION
);
if
(
l
oad
Db
(
server
.
dbfilename
)
==
REDIS_OK
)
if
(
rdbL
oad
(
server
.
dbfilename
)
==
REDIS_OK
)
redisLog
(
REDIS_NOTICE
,
"DB loaded from disk"
);
redisLog
(
REDIS_NOTICE
,
"DB loaded from disk"
);
if
(
aeCreateFileEvent
(
server
.
el
,
server
.
fd
,
AE_READABLE
,
if
(
aeCreateFileEvent
(
server
.
el
,
server
.
fd
,
AE_READABLE
,
acceptHandler
,
NULL
,
NULL
)
==
AE_ERR
)
oom
(
"creating file event"
);
acceptHandler
,
NULL
,
NULL
)
==
AE_ERR
)
oom
(
"creating file event"
);
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment