redis-server 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/LICENSE +38 -0
- data/README.md +33 -0
- data/bin/redis +114 -0
- data/redis/Makefile +5 -0
- data/redis/extconf.rb +3 -0
- data/redis/redis-2.2.11/00-RELEASENOTES +199 -0
- data/redis/redis-2.2.11/BUGS +1 -0
- data/redis/redis-2.2.11/CONTRIBUTING +13 -0
- data/redis/redis-2.2.11/COPYING +10 -0
- data/redis/redis-2.2.11/Changelog +1032 -0
- data/redis/redis-2.2.11/INSTALL +30 -0
- data/redis/redis-2.2.11/Makefile +22 -0
- data/redis/redis-2.2.11/README +83 -0
- data/redis/redis-2.2.11/TODO +4 -0
- data/redis/redis-2.2.11/client-libraries/README +11 -0
- data/redis/redis-2.2.11/deps/hiredis/COPYING +10 -0
- data/redis/redis-2.2.11/deps/hiredis/Makefile +115 -0
- data/redis/redis-2.2.11/deps/hiredis/README.md +311 -0
- data/redis/redis-2.2.11/deps/hiredis/TODO +2 -0
- data/redis/redis-2.2.11/deps/hiredis/adapters/ae.h +95 -0
- data/redis/redis-2.2.11/deps/hiredis/adapters/libev.h +113 -0
- data/redis/redis-2.2.11/deps/hiredis/adapters/libevent.h +76 -0
- data/redis/redis-2.2.11/deps/hiredis/async.c +321 -0
- data/redis/redis-2.2.11/deps/hiredis/async.h +112 -0
- data/redis/redis-2.2.11/deps/hiredis/example-ae.c +53 -0
- data/redis/redis-2.2.11/deps/hiredis/example-libev.c +47 -0
- data/redis/redis-2.2.11/deps/hiredis/example-libevent.c +48 -0
- data/redis/redis-2.2.11/deps/hiredis/example.c +67 -0
- data/redis/redis-2.2.11/deps/hiredis/fmacros.h +15 -0
- data/redis/redis-2.2.11/deps/hiredis/hiredis.c +1058 -0
- data/redis/redis-2.2.11/deps/hiredis/hiredis.h +170 -0
- data/redis/redis-2.2.11/deps/hiredis/net.c +170 -0
- data/redis/redis-2.2.11/deps/hiredis/net.h +43 -0
- data/redis/redis-2.2.11/deps/hiredis/sds.c +479 -0
- data/redis/redis-2.2.11/deps/hiredis/sds.h +77 -0
- data/redis/redis-2.2.11/deps/hiredis/test.c +479 -0
- data/redis/redis-2.2.11/deps/hiredis/util.h +40 -0
- data/redis/redis-2.2.11/deps/linenoise/Makefile +10 -0
- data/redis/redis-2.2.11/deps/linenoise/README.markdown +45 -0
- data/redis/redis-2.2.11/deps/linenoise/example.c +27 -0
- data/redis/redis-2.2.11/deps/linenoise/linenoise.c +609 -0
- data/redis/redis-2.2.11/deps/linenoise/linenoise.h +55 -0
- data/redis/redis-2.2.11/design-documents/REDIS-CLUSTER +214 -0
- data/redis/redis-2.2.11/design-documents/REDIS-CLUSTER-2 +343 -0
- data/redis/redis-2.2.11/doc/AppendCommand.html +48 -0
- data/redis/redis-2.2.11/doc/AppendOnlyFileHowto.html +41 -0
- data/redis/redis-2.2.11/doc/AuthCommand.html +39 -0
- data/redis/redis-2.2.11/doc/Benchmarks.html +129 -0
- data/redis/redis-2.2.11/doc/BgrewriteaofCommand.html +41 -0
- data/redis/redis-2.2.11/doc/BgsaveCommand.html +39 -0
- data/redis/redis-2.2.11/doc/BlpopCommand.html +51 -0
- data/redis/redis-2.2.11/doc/BrpoplpushCommand.html +39 -0
- data/redis/redis-2.2.11/doc/CommandReference.html +47 -0
- data/redis/redis-2.2.11/doc/Comparisons.html +42 -0
- data/redis/redis-2.2.11/doc/ConfigCommand.html +76 -0
- data/redis/redis-2.2.11/doc/Configuration.html +38 -0
- data/redis/redis-2.2.11/doc/ConnectionHandlingSidebar.html +36 -0
- data/redis/redis-2.2.11/doc/ControlCommandsSidebar.html +36 -0
- data/redis/redis-2.2.11/doc/Credits.html +38 -0
- data/redis/redis-2.2.11/doc/DbsizeCommand.html +38 -0
- data/redis/redis-2.2.11/doc/DelCommand.html +41 -0
- data/redis/redis-2.2.11/doc/DesignPatterns.html +37 -0
- data/redis/redis-2.2.11/doc/EventLibray.html +44 -0
- data/redis/redis-2.2.11/doc/ExistsCommand.html +42 -0
- data/redis/redis-2.2.11/doc/ExpireCommand.html +96 -0
- data/redis/redis-2.2.11/doc/FAQ.html +70 -0
- data/redis/redis-2.2.11/doc/Features.html +38 -0
- data/redis/redis-2.2.11/doc/FlushallCommand.html +39 -0
- data/redis/redis-2.2.11/doc/FlushdbCommand.html +39 -0
- data/redis/redis-2.2.11/doc/FromSqlToDataStructures.html +37 -0
- data/redis/redis-2.2.11/doc/GenericCommandsSidebar.html +36 -0
- data/redis/redis-2.2.11/doc/GetCommand.html +39 -0
- data/redis/redis-2.2.11/doc/GetbitCommand.html +39 -0
- data/redis/redis-2.2.11/doc/GetsetCommand.html +38 -0
- data/redis/redis-2.2.11/doc/HackingStrings.html +83 -0
- data/redis/redis-2.2.11/doc/HashCommandsSidebar.html +36 -0
- data/redis/redis-2.2.11/doc/Hashes.html +37 -0
- data/redis/redis-2.2.11/doc/HdelCommand.html +39 -0
- data/redis/redis-2.2.11/doc/HexistsCommand.html +39 -0
- data/redis/redis-2.2.11/doc/HgetCommand.html +39 -0
- data/redis/redis-2.2.11/doc/HgetallCommand.html +40 -0
- data/redis/redis-2.2.11/doc/HincrbyCommand.html +45 -0
- data/redis/redis-2.2.11/doc/HlenCommand.html +38 -0
- data/redis/redis-2.2.11/doc/HmgetCommand.html +40 -0
- data/redis/redis-2.2.11/doc/HmsetCommand.html +40 -0
- data/redis/redis-2.2.11/doc/HsetCommand.html +40 -0
- data/redis/redis-2.2.11/doc/HsetnxCommand.html +41 -0
- data/redis/redis-2.2.11/doc/IncrCommand.html +43 -0
- data/redis/redis-2.2.11/doc/InfoCommand.html +48 -0
- data/redis/redis-2.2.11/doc/IntroductionToRedisDataTypes.html +152 -0
- data/redis/redis-2.2.11/doc/KeysCommand.html +43 -0
- data/redis/redis-2.2.11/doc/LastsaveCommand.html +39 -0
- data/redis/redis-2.2.11/doc/LindexCommand.html +40 -0
- data/redis/redis-2.2.11/doc/ListCommandsSidebar.html +36 -0
- data/redis/redis-2.2.11/doc/Lists.html +42 -0
- data/redis/redis-2.2.11/doc/LlenCommand.html +41 -0
- data/redis/redis-2.2.11/doc/LpopCommand.html +41 -0
- data/redis/redis-2.2.11/doc/LrangeCommand.html +47 -0
- data/redis/redis-2.2.11/doc/LremCommand.html +41 -0
- data/redis/redis-2.2.11/doc/LsetCommand.html +38 -0
- data/redis/redis-2.2.11/doc/LtrimCommand.html +47 -0
- data/redis/redis-2.2.11/doc/MgetCommand.html +52 -0
- data/redis/redis-2.2.11/doc/MonitorCommand.html +63 -0
- data/redis/redis-2.2.11/doc/MoveCommand.html +42 -0
- data/redis/redis-2.2.11/doc/MsetCommand.html +44 -0
- data/redis/redis-2.2.11/doc/MultiExecCommand.html +166 -0
- data/redis/redis-2.2.11/doc/NonexistentCommands.html +51 -0
- data/redis/redis-2.2.11/doc/ObjectHashMappers.html +39 -0
- data/redis/redis-2.2.11/doc/Pipelining.html +36 -0
- data/redis/redis-2.2.11/doc/ProgrammingExamples.html +38 -0
- data/redis/redis-2.2.11/doc/ProtocolSpecification.html +137 -0
- data/redis/redis-2.2.11/doc/PublishSubscribe.html +115 -0
- data/redis/redis-2.2.11/doc/QuickStart.html +68 -0
- data/redis/redis-2.2.11/doc/QuitCommand.html +38 -0
- data/redis/redis-2.2.11/doc/README.html +119 -0
- data/redis/redis-2.2.11/doc/RandomkeyCommand.html +39 -0
- data/redis/redis-2.2.11/doc/Redis0100ChangeLog.html +67 -0
- data/redis/redis-2.2.11/doc/Redis0900ChangeLog.html +56 -0
- data/redis/redis-2.2.11/doc/RedisBigData.html +61 -0
- data/redis/redis-2.2.11/doc/RedisCLI.html +37 -0
- data/redis/redis-2.2.11/doc/RedisEventLibrary.html +70 -0
- data/redis/redis-2.2.11/doc/RedisGuides.html +37 -0
- data/redis/redis-2.2.11/doc/RedisInternals.html +38 -0
- data/redis/redis-2.2.11/doc/RedisPipelining.html +93 -0
- data/redis/redis-2.2.11/doc/RedisStatus.html +56 -0
- data/redis/redis-2.2.11/doc/Redis_1_2_0_Changelog.html +40 -0
- data/redis/redis-2.2.11/doc/Redis_2_0_0_Changelog.html +62 -0
- data/redis/redis-2.2.11/doc/Redis_2_0_Whats_new.html +59 -0
- data/redis/redis-2.2.11/doc/RenameCommand.html +39 -0
- data/redis/redis-2.2.11/doc/RenamenxCommand.html +42 -0
- data/redis/redis-2.2.11/doc/ReplicationHowto.html +43 -0
- data/redis/redis-2.2.11/doc/ReplyTypes.html +42 -0
- data/redis/redis-2.2.11/doc/RoadMap.html +38 -0
- data/redis/redis-2.2.11/doc/RpoplpushCommand.html +44 -0
- data/redis/redis-2.2.11/doc/RpushCommand.html +40 -0
- data/redis/redis-2.2.11/doc/SaddCommand.html +41 -0
- data/redis/redis-2.2.11/doc/SaveCommand.html +39 -0
- data/redis/redis-2.2.11/doc/ScardCommand.html +41 -0
- data/redis/redis-2.2.11/doc/SdiffCommand.html +45 -0
- data/redis/redis-2.2.11/doc/SdiffstoreCommand.html +38 -0
- data/redis/redis-2.2.11/doc/SelectCommand.html +39 -0
- data/redis/redis-2.2.11/doc/SetCommand.html +39 -0
- data/redis/redis-2.2.11/doc/SetCommandsSidebar.html +36 -0
- data/redis/redis-2.2.11/doc/SetbitCommand.html +45 -0
- data/redis/redis-2.2.11/doc/SetexCommand.html +42 -0
- data/redis/redis-2.2.11/doc/SetnxCommand.html +51 -0
- data/redis/redis-2.2.11/doc/SetrangeCommand.html +58 -0
- data/redis/redis-2.2.11/doc/Sets.html +36 -0
- data/redis/redis-2.2.11/doc/ShutdownCommand.html +39 -0
- data/redis/redis-2.2.11/doc/SideBar.html +36 -0
- data/redis/redis-2.2.11/doc/SinterCommand.html +40 -0
- data/redis/redis-2.2.11/doc/SinterstoreCommand.html +39 -0
- data/redis/redis-2.2.11/doc/SismemberCommand.html +42 -0
- data/redis/redis-2.2.11/doc/SlaveofCommand.html +41 -0
- data/redis/redis-2.2.11/doc/SmembersCommand.html +38 -0
- data/redis/redis-2.2.11/doc/SmoveCommand.html +44 -0
- data/redis/redis-2.2.11/doc/SortCommand.html +75 -0
- data/redis/redis-2.2.11/doc/SortedSetCommandsSidebar.html +36 -0
- data/redis/redis-2.2.11/doc/SortedSets.html +36 -0
- data/redis/redis-2.2.11/doc/Speed.html +38 -0
- data/redis/redis-2.2.11/doc/SponsorshipHistory.html +38 -0
- data/redis/redis-2.2.11/doc/SpopCommand.html +40 -0
- data/redis/redis-2.2.11/doc/SrandmemberCommand.html +40 -0
- data/redis/redis-2.2.11/doc/SremCommand.html +42 -0
- data/redis/redis-2.2.11/doc/StringCommandsSidebar.html +36 -0
- data/redis/redis-2.2.11/doc/Strings.html +37 -0
- data/redis/redis-2.2.11/doc/StrlenCommand.html +39 -0
- data/redis/redis-2.2.11/doc/SubstrCommand.html +52 -0
- data/redis/redis-2.2.11/doc/SunionCommand.html +40 -0
- data/redis/redis-2.2.11/doc/SunionstoreCommand.html +38 -0
- data/redis/redis-2.2.11/doc/SupportedLanguages.html +60 -0
- data/redis/redis-2.2.11/doc/SupportedPlatforms.html +37 -0
- data/redis/redis-2.2.11/doc/TemplateCommand.html +38 -0
- data/redis/redis-2.2.11/doc/TtlCommand.html +38 -0
- data/redis/redis-2.2.11/doc/TwitterAlikeExample.html +250 -0
- data/redis/redis-2.2.11/doc/TypeCommand.html +46 -0
- data/redis/redis-2.2.11/doc/UnstableSource.html +39 -0
- data/redis/redis-2.2.11/doc/VirtualMemorySpecification.html +156 -0
- data/redis/redis-2.2.11/doc/VirtualMemoryUserGuide.html +66 -0
- data/redis/redis-2.2.11/doc/ZaddCommand.html +43 -0
- data/redis/redis-2.2.11/doc/ZcardCommand.html +41 -0
- data/redis/redis-2.2.11/doc/ZincrbyCommand.html +42 -0
- data/redis/redis-2.2.11/doc/ZrangeCommand.html +42 -0
- data/redis/redis-2.2.11/doc/ZrangebyscoreCommand.html +77 -0
- data/redis/redis-2.2.11/doc/ZrankCommand.html +43 -0
- data/redis/redis-2.2.11/doc/ZremCommand.html +42 -0
- data/redis/redis-2.2.11/doc/ZremrangebyrankCommand.html +39 -0
- data/redis/redis-2.2.11/doc/ZremrangebyscoreCommand.html +39 -0
- data/redis/redis-2.2.11/doc/ZscoreCommand.html +41 -0
- data/redis/redis-2.2.11/doc/ZunionCommand.html +42 -0
- data/redis/redis-2.2.11/doc/ZunionstoreCommand.html +43 -0
- data/redis/redis-2.2.11/doc/index.html +43 -0
- data/redis/redis-2.2.11/doc/redis.png +0 -0
- data/redis/redis-2.2.11/doc/style.css +25 -0
- data/redis/redis-2.2.11/redis.conf +417 -0
- data/redis/redis-2.2.11/src/Makefile +177 -0
- data/redis/redis-2.2.11/src/adlist.c +325 -0
- data/redis/redis-2.2.11/src/adlist.h +92 -0
- data/redis/redis-2.2.11/src/ae.c +390 -0
- data/redis/redis-2.2.11/src/ae.h +117 -0
- data/redis/redis-2.2.11/src/ae_epoll.c +91 -0
- data/redis/redis-2.2.11/src/ae_kqueue.c +93 -0
- data/redis/redis-2.2.11/src/ae_select.c +72 -0
- data/redis/redis-2.2.11/src/anet.c +347 -0
- data/redis/redis-2.2.11/src/anet.h +57 -0
- data/redis/redis-2.2.11/src/aof.c +675 -0
- data/redis/redis-2.2.11/src/config.c +627 -0
- data/redis/redis-2.2.11/src/config.h +64 -0
- data/redis/redis-2.2.11/src/db.c +543 -0
- data/redis/redis-2.2.11/src/debug.c +314 -0
- data/redis/redis-2.2.11/src/dict.c +721 -0
- data/redis/redis-2.2.11/src/dict.h +156 -0
- data/redis/redis-2.2.11/src/fmacros.h +15 -0
- data/redis/redis-2.2.11/src/help.h +638 -0
- data/redis/redis-2.2.11/src/intset.c +422 -0
- data/redis/redis-2.2.11/src/intset.h +19 -0
- data/redis/redis-2.2.11/src/lzf.h +100 -0
- data/redis/redis-2.2.11/src/lzfP.h +159 -0
- data/redis/redis-2.2.11/src/lzf_c.c +295 -0
- data/redis/redis-2.2.11/src/lzf_d.c +150 -0
- data/redis/redis-2.2.11/src/mkreleasehdr.sh +9 -0
- data/redis/redis-2.2.11/src/multi.c +268 -0
- data/redis/redis-2.2.11/src/networking.c +899 -0
- data/redis/redis-2.2.11/src/object.c +484 -0
- data/redis/redis-2.2.11/src/pqsort.c +197 -0
- data/redis/redis-2.2.11/src/pqsort.h +15 -0
- data/redis/redis-2.2.11/src/pubsub.c +267 -0
- data/redis/redis-2.2.11/src/rdb.c +1020 -0
- data/redis/redis-2.2.11/src/redis-benchmark.c +530 -0
- data/redis/redis-2.2.11/src/redis-check-aof.c +185 -0
- data/redis/redis-2.2.11/src/redis-check-dump.c +681 -0
- data/redis/redis-2.2.11/src/redis-cli.c +773 -0
- data/redis/redis-2.2.11/src/redis.c +1677 -0
- data/redis/redis-2.2.11/src/redis.h +1022 -0
- data/redis/redis-2.2.11/src/release.c +13 -0
- data/redis/redis-2.2.11/src/replication.c +557 -0
- data/redis/redis-2.2.11/src/sds.c +639 -0
- data/redis/redis-2.2.11/src/sds.h +78 -0
- data/redis/redis-2.2.11/src/sha1.c +276 -0
- data/redis/redis-2.2.11/src/sha1.h +17 -0
- data/redis/redis-2.2.11/src/solarisfixes.h +22 -0
- data/redis/redis-2.2.11/src/sort.c +389 -0
- data/redis/redis-2.2.11/src/syncio.c +154 -0
- data/redis/redis-2.2.11/src/t_hash.c +476 -0
- data/redis/redis-2.2.11/src/t_list.c +986 -0
- data/redis/redis-2.2.11/src/t_set.c +610 -0
- data/redis/redis-2.2.11/src/t_string.c +438 -0
- data/redis/redis-2.2.11/src/t_zset.c +1084 -0
- data/redis/redis-2.2.11/src/testhelp.h +54 -0
- data/redis/redis-2.2.11/src/util.c +243 -0
- data/redis/redis-2.2.11/src/valgrind.sup +5 -0
- data/redis/redis-2.2.11/src/version.h +1 -0
- data/redis/redis-2.2.11/src/vm.c +1149 -0
- data/redis/redis-2.2.11/src/ziplist.c +1323 -0
- data/redis/redis-2.2.11/src/ziplist.h +15 -0
- data/redis/redis-2.2.11/src/zipmap.c +455 -0
- data/redis/redis-2.2.11/src/zipmap.h +48 -0
- data/redis/redis-2.2.11/src/zmalloc.c +278 -0
- data/redis/redis-2.2.11/src/zmalloc.h +47 -0
- data/redis/redis-2.2.11/tests/assets/default.conf +308 -0
- data/redis/redis-2.2.11/tests/integration/aof.tcl +104 -0
- data/redis/redis-2.2.11/tests/integration/redis-cli.tcl +208 -0
- data/redis/redis-2.2.11/tests/integration/replication.tcl +98 -0
- data/redis/redis-2.2.11/tests/support/redis.tcl +241 -0
- data/redis/redis-2.2.11/tests/support/server.tcl +294 -0
- data/redis/redis-2.2.11/tests/support/test.tcl +190 -0
- data/redis/redis-2.2.11/tests/support/tmpfile.tcl +15 -0
- data/redis/redis-2.2.11/tests/support/util.tcl +296 -0
- data/redis/redis-2.2.11/tests/test_helper.tcl +221 -0
- data/redis/redis-2.2.11/tests/unit/auth.tcl +15 -0
- data/redis/redis-2.2.11/tests/unit/basic.tcl +616 -0
- data/redis/redis-2.2.11/tests/unit/cas.tcl +135 -0
- data/redis/redis-2.2.11/tests/unit/expire.tcl +74 -0
- data/redis/redis-2.2.11/tests/unit/other.tcl +240 -0
- data/redis/redis-2.2.11/tests/unit/printver.tcl +6 -0
- data/redis/redis-2.2.11/tests/unit/protocol.tcl +62 -0
- data/redis/redis-2.2.11/tests/unit/pubsub.tcl +195 -0
- data/redis/redis-2.2.11/tests/unit/quit.tcl +40 -0
- data/redis/redis-2.2.11/tests/unit/sort.tcl +189 -0
- data/redis/redis-2.2.11/tests/unit/type/hash.tcl +300 -0
- data/redis/redis-2.2.11/tests/unit/type/list.tcl +819 -0
- data/redis/redis-2.2.11/tests/unit/type/set.tcl +334 -0
- data/redis/redis-2.2.11/tests/unit/type/zset.tcl +587 -0
- data/redis/redis-2.2.11/utils/build-static-symbols.tcl +22 -0
- data/redis/redis-2.2.11/utils/generate-command-help.rb +112 -0
- data/redis/redis-2.2.11/utils/mktarball.sh +13 -0
- data/redis/redis-2.2.11/utils/redis-copy.rb +78 -0
- data/redis/redis-2.2.11/utils/redis-sha1.rb +52 -0
- data/redis/redis-2.2.11/utils/redis_init_script +42 -0
- metadata +362 -0
@@ -0,0 +1,610 @@
|
|
1
|
+
#include "redis.h"
|
2
|
+
|
3
|
+
/*-----------------------------------------------------------------------------
|
4
|
+
* Set Commands
|
5
|
+
*----------------------------------------------------------------------------*/
|
6
|
+
|
7
|
+
/* Factory method to return a set that *can* hold "value". When the object has
|
8
|
+
* an integer-encodable value, an intset will be returned. Otherwise a regular
|
9
|
+
* hash table. */
|
10
|
+
robj *setTypeCreate(robj *value) {
|
11
|
+
if (isObjectRepresentableAsLongLong(value,NULL) == REDIS_OK)
|
12
|
+
return createIntsetObject();
|
13
|
+
return createSetObject();
|
14
|
+
}
|
15
|
+
|
16
|
+
int setTypeAdd(robj *subject, robj *value) {
|
17
|
+
long long llval;
|
18
|
+
if (subject->encoding == REDIS_ENCODING_HT) {
|
19
|
+
if (dictAdd(subject->ptr,value,NULL) == DICT_OK) {
|
20
|
+
incrRefCount(value);
|
21
|
+
return 1;
|
22
|
+
}
|
23
|
+
} else if (subject->encoding == REDIS_ENCODING_INTSET) {
|
24
|
+
if (isObjectRepresentableAsLongLong(value,&llval) == REDIS_OK) {
|
25
|
+
uint8_t success = 0;
|
26
|
+
subject->ptr = intsetAdd(subject->ptr,llval,&success);
|
27
|
+
if (success) {
|
28
|
+
/* Convert to regular set when the intset contains
|
29
|
+
* too many entries. */
|
30
|
+
if (intsetLen(subject->ptr) > server.set_max_intset_entries)
|
31
|
+
setTypeConvert(subject,REDIS_ENCODING_HT);
|
32
|
+
return 1;
|
33
|
+
}
|
34
|
+
} else {
|
35
|
+
/* Failed to get integer from object, convert to regular set. */
|
36
|
+
setTypeConvert(subject,REDIS_ENCODING_HT);
|
37
|
+
|
38
|
+
/* The set *was* an intset and this value is not integer
|
39
|
+
* encodable, so dictAdd should always work. */
|
40
|
+
redisAssert(dictAdd(subject->ptr,value,NULL) == DICT_OK);
|
41
|
+
incrRefCount(value);
|
42
|
+
return 1;
|
43
|
+
}
|
44
|
+
} else {
|
45
|
+
redisPanic("Unknown set encoding");
|
46
|
+
}
|
47
|
+
return 0;
|
48
|
+
}
|
49
|
+
|
50
|
+
int setTypeRemove(robj *setobj, robj *value) {
|
51
|
+
long long llval;
|
52
|
+
if (setobj->encoding == REDIS_ENCODING_HT) {
|
53
|
+
if (dictDelete(setobj->ptr,value) == DICT_OK) {
|
54
|
+
if (htNeedsResize(setobj->ptr)) dictResize(setobj->ptr);
|
55
|
+
return 1;
|
56
|
+
}
|
57
|
+
} else if (setobj->encoding == REDIS_ENCODING_INTSET) {
|
58
|
+
if (isObjectRepresentableAsLongLong(value,&llval) == REDIS_OK) {
|
59
|
+
int success;
|
60
|
+
setobj->ptr = intsetRemove(setobj->ptr,llval,&success);
|
61
|
+
if (success) return 1;
|
62
|
+
}
|
63
|
+
} else {
|
64
|
+
redisPanic("Unknown set encoding");
|
65
|
+
}
|
66
|
+
return 0;
|
67
|
+
}
|
68
|
+
|
69
|
+
int setTypeIsMember(robj *subject, robj *value) {
|
70
|
+
long long llval;
|
71
|
+
if (subject->encoding == REDIS_ENCODING_HT) {
|
72
|
+
return dictFind((dict*)subject->ptr,value) != NULL;
|
73
|
+
} else if (subject->encoding == REDIS_ENCODING_INTSET) {
|
74
|
+
if (isObjectRepresentableAsLongLong(value,&llval) == REDIS_OK) {
|
75
|
+
return intsetFind((intset*)subject->ptr,llval);
|
76
|
+
}
|
77
|
+
} else {
|
78
|
+
redisPanic("Unknown set encoding");
|
79
|
+
}
|
80
|
+
return 0;
|
81
|
+
}
|
82
|
+
|
83
|
+
setTypeIterator *setTypeInitIterator(robj *subject) {
|
84
|
+
setTypeIterator *si = zmalloc(sizeof(setTypeIterator));
|
85
|
+
si->subject = subject;
|
86
|
+
si->encoding = subject->encoding;
|
87
|
+
if (si->encoding == REDIS_ENCODING_HT) {
|
88
|
+
si->di = dictGetIterator(subject->ptr);
|
89
|
+
} else if (si->encoding == REDIS_ENCODING_INTSET) {
|
90
|
+
si->ii = 0;
|
91
|
+
} else {
|
92
|
+
redisPanic("Unknown set encoding");
|
93
|
+
}
|
94
|
+
return si;
|
95
|
+
}
|
96
|
+
|
97
|
+
void setTypeReleaseIterator(setTypeIterator *si) {
|
98
|
+
if (si->encoding == REDIS_ENCODING_HT)
|
99
|
+
dictReleaseIterator(si->di);
|
100
|
+
zfree(si);
|
101
|
+
}
|
102
|
+
|
103
|
+
/* Move to the next entry in the set. Returns the object at the current
|
104
|
+
* position.
|
105
|
+
*
|
106
|
+
* Since set elements can be internally be stored as redis objects or
|
107
|
+
* simple arrays of integers, setTypeNext returns the encoding of the
|
108
|
+
* set object you are iterating, and will populate the appropriate pointer
|
109
|
+
* (eobj) or (llobj) accordingly.
|
110
|
+
*
|
111
|
+
* When there are no longer elements -1 is returned.
|
112
|
+
* Returned objects ref count is not incremented, so this function is
|
113
|
+
* copy on write friendly. */
|
114
|
+
int setTypeNext(setTypeIterator *si, robj **objele, int64_t *llele) {
|
115
|
+
if (si->encoding == REDIS_ENCODING_HT) {
|
116
|
+
dictEntry *de = dictNext(si->di);
|
117
|
+
if (de == NULL) return -1;
|
118
|
+
*objele = dictGetEntryKey(de);
|
119
|
+
} else if (si->encoding == REDIS_ENCODING_INTSET) {
|
120
|
+
if (!intsetGet(si->subject->ptr,si->ii++,llele))
|
121
|
+
return -1;
|
122
|
+
}
|
123
|
+
return si->encoding;
|
124
|
+
}
|
125
|
+
|
126
|
+
/* The not copy on write friendly version but easy to use version
|
127
|
+
* of setTypeNext() is setTypeNextObject(), returning new objects
|
128
|
+
* or incrementing the ref count of returned objects. So if you don't
|
129
|
+
* retain a pointer to this object you should call decrRefCount() against it.
|
130
|
+
*
|
131
|
+
* This function is the way to go for write operations where COW is not
|
132
|
+
* an issue as the result will be anyway of incrementing the ref count. */
|
133
|
+
robj *setTypeNextObject(setTypeIterator *si) {
|
134
|
+
int64_t intele;
|
135
|
+
robj *objele;
|
136
|
+
int encoding;
|
137
|
+
|
138
|
+
encoding = setTypeNext(si,&objele,&intele);
|
139
|
+
switch(encoding) {
|
140
|
+
case -1: return NULL;
|
141
|
+
case REDIS_ENCODING_INTSET:
|
142
|
+
return createStringObjectFromLongLong(intele);
|
143
|
+
case REDIS_ENCODING_HT:
|
144
|
+
incrRefCount(objele);
|
145
|
+
return objele;
|
146
|
+
default:
|
147
|
+
redisPanic("Unsupported encoding");
|
148
|
+
}
|
149
|
+
return NULL; /* just to suppress warnings */
|
150
|
+
}
|
151
|
+
|
152
|
+
/* Return random element from a non empty set.
|
153
|
+
* The returned element can be a int64_t value if the set is encoded
|
154
|
+
* as an "intset" blob of integers, or a redis object if the set
|
155
|
+
* is a regular set.
|
156
|
+
*
|
157
|
+
* The caller provides both pointers to be populated with the right
|
158
|
+
* object. The return value of the function is the object->encoding
|
159
|
+
* field of the object and is used by the caller to check if the
|
160
|
+
* int64_t pointer or the redis object pointere was populated.
|
161
|
+
*
|
162
|
+
* When an object is returned (the set was a real set) the ref count
|
163
|
+
* of the object is not incremented so this function can be considered
|
164
|
+
* copy on write friendly. */
|
165
|
+
int setTypeRandomElement(robj *setobj, robj **objele, int64_t *llele) {
|
166
|
+
if (setobj->encoding == REDIS_ENCODING_HT) {
|
167
|
+
dictEntry *de = dictGetRandomKey(setobj->ptr);
|
168
|
+
*objele = dictGetEntryKey(de);
|
169
|
+
} else if (setobj->encoding == REDIS_ENCODING_INTSET) {
|
170
|
+
*llele = intsetRandom(setobj->ptr);
|
171
|
+
} else {
|
172
|
+
redisPanic("Unknown set encoding");
|
173
|
+
}
|
174
|
+
return setobj->encoding;
|
175
|
+
}
|
176
|
+
|
177
|
+
unsigned long setTypeSize(robj *subject) {
|
178
|
+
if (subject->encoding == REDIS_ENCODING_HT) {
|
179
|
+
return dictSize((dict*)subject->ptr);
|
180
|
+
} else if (subject->encoding == REDIS_ENCODING_INTSET) {
|
181
|
+
return intsetLen((intset*)subject->ptr);
|
182
|
+
} else {
|
183
|
+
redisPanic("Unknown set encoding");
|
184
|
+
}
|
185
|
+
}
|
186
|
+
|
187
|
+
/* Convert the set to specified encoding. The resulting dict (when converting
|
188
|
+
* to a hashtable) is presized to hold the number of elements in the original
|
189
|
+
* set. */
|
190
|
+
void setTypeConvert(robj *setobj, int enc) {
|
191
|
+
setTypeIterator *si;
|
192
|
+
redisAssert(setobj->type == REDIS_SET &&
|
193
|
+
setobj->encoding == REDIS_ENCODING_INTSET);
|
194
|
+
|
195
|
+
if (enc == REDIS_ENCODING_HT) {
|
196
|
+
int64_t intele;
|
197
|
+
dict *d = dictCreate(&setDictType,NULL);
|
198
|
+
robj *element;
|
199
|
+
|
200
|
+
/* Presize the dict to avoid rehashing */
|
201
|
+
dictExpand(d,intsetLen(setobj->ptr));
|
202
|
+
|
203
|
+
/* To add the elements we extract integers and create redis objects */
|
204
|
+
si = setTypeInitIterator(setobj);
|
205
|
+
while (setTypeNext(si,NULL,&intele) != -1) {
|
206
|
+
element = createStringObjectFromLongLong(intele);
|
207
|
+
redisAssert(dictAdd(d,element,NULL) == DICT_OK);
|
208
|
+
}
|
209
|
+
setTypeReleaseIterator(si);
|
210
|
+
|
211
|
+
setobj->encoding = REDIS_ENCODING_HT;
|
212
|
+
zfree(setobj->ptr);
|
213
|
+
setobj->ptr = d;
|
214
|
+
} else {
|
215
|
+
redisPanic("Unsupported set conversion");
|
216
|
+
}
|
217
|
+
}
|
218
|
+
|
219
|
+
void saddCommand(redisClient *c) {
|
220
|
+
robj *set;
|
221
|
+
|
222
|
+
set = lookupKeyWrite(c->db,c->argv[1]);
|
223
|
+
c->argv[2] = tryObjectEncoding(c->argv[2]);
|
224
|
+
if (set == NULL) {
|
225
|
+
set = setTypeCreate(c->argv[2]);
|
226
|
+
dbAdd(c->db,c->argv[1],set);
|
227
|
+
} else {
|
228
|
+
if (set->type != REDIS_SET) {
|
229
|
+
addReply(c,shared.wrongtypeerr);
|
230
|
+
return;
|
231
|
+
}
|
232
|
+
}
|
233
|
+
if (setTypeAdd(set,c->argv[2])) {
|
234
|
+
touchWatchedKey(c->db,c->argv[1]);
|
235
|
+
server.dirty++;
|
236
|
+
addReply(c,shared.cone);
|
237
|
+
} else {
|
238
|
+
addReply(c,shared.czero);
|
239
|
+
}
|
240
|
+
}
|
241
|
+
|
242
|
+
void sremCommand(redisClient *c) {
|
243
|
+
robj *set;
|
244
|
+
|
245
|
+
if ((set = lookupKeyWriteOrReply(c,c->argv[1],shared.czero)) == NULL ||
|
246
|
+
checkType(c,set,REDIS_SET)) return;
|
247
|
+
|
248
|
+
c->argv[2] = tryObjectEncoding(c->argv[2]);
|
249
|
+
if (setTypeRemove(set,c->argv[2])) {
|
250
|
+
if (setTypeSize(set) == 0) dbDelete(c->db,c->argv[1]);
|
251
|
+
touchWatchedKey(c->db,c->argv[1]);
|
252
|
+
server.dirty++;
|
253
|
+
addReply(c,shared.cone);
|
254
|
+
} else {
|
255
|
+
addReply(c,shared.czero);
|
256
|
+
}
|
257
|
+
}
|
258
|
+
|
259
|
+
void smoveCommand(redisClient *c) {
|
260
|
+
robj *srcset, *dstset, *ele;
|
261
|
+
srcset = lookupKeyWrite(c->db,c->argv[1]);
|
262
|
+
dstset = lookupKeyWrite(c->db,c->argv[2]);
|
263
|
+
ele = c->argv[3] = tryObjectEncoding(c->argv[3]);
|
264
|
+
|
265
|
+
/* If the source key does not exist return 0 */
|
266
|
+
if (srcset == NULL) {
|
267
|
+
addReply(c,shared.czero);
|
268
|
+
return;
|
269
|
+
}
|
270
|
+
|
271
|
+
/* If the source key has the wrong type, or the destination key
|
272
|
+
* is set and has the wrong type, return with an error. */
|
273
|
+
if (checkType(c,srcset,REDIS_SET) ||
|
274
|
+
(dstset && checkType(c,dstset,REDIS_SET))) return;
|
275
|
+
|
276
|
+
/* If srcset and dstset are equal, SMOVE is a no-op */
|
277
|
+
if (srcset == dstset) {
|
278
|
+
addReply(c,shared.cone);
|
279
|
+
return;
|
280
|
+
}
|
281
|
+
|
282
|
+
/* If the element cannot be removed from the src set, return 0. */
|
283
|
+
if (!setTypeRemove(srcset,ele)) {
|
284
|
+
addReply(c,shared.czero);
|
285
|
+
return;
|
286
|
+
}
|
287
|
+
|
288
|
+
/* Remove the src set from the database when empty */
|
289
|
+
if (setTypeSize(srcset) == 0) dbDelete(c->db,c->argv[1]);
|
290
|
+
touchWatchedKey(c->db,c->argv[1]);
|
291
|
+
touchWatchedKey(c->db,c->argv[2]);
|
292
|
+
server.dirty++;
|
293
|
+
|
294
|
+
/* Create the destination set when it doesn't exist */
|
295
|
+
if (!dstset) {
|
296
|
+
dstset = setTypeCreate(ele);
|
297
|
+
dbAdd(c->db,c->argv[2],dstset);
|
298
|
+
}
|
299
|
+
|
300
|
+
/* An extra key has changed when ele was successfully added to dstset */
|
301
|
+
if (setTypeAdd(dstset,ele)) server.dirty++;
|
302
|
+
addReply(c,shared.cone);
|
303
|
+
}
|
304
|
+
|
305
|
+
void sismemberCommand(redisClient *c) {
|
306
|
+
robj *set;
|
307
|
+
|
308
|
+
if ((set = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
|
309
|
+
checkType(c,set,REDIS_SET)) return;
|
310
|
+
|
311
|
+
c->argv[2] = tryObjectEncoding(c->argv[2]);
|
312
|
+
if (setTypeIsMember(set,c->argv[2]))
|
313
|
+
addReply(c,shared.cone);
|
314
|
+
else
|
315
|
+
addReply(c,shared.czero);
|
316
|
+
}
|
317
|
+
|
318
|
+
void scardCommand(redisClient *c) {
|
319
|
+
robj *o;
|
320
|
+
|
321
|
+
if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
|
322
|
+
checkType(c,o,REDIS_SET)) return;
|
323
|
+
|
324
|
+
addReplyLongLong(c,setTypeSize(o));
|
325
|
+
}
|
326
|
+
|
327
|
+
void spopCommand(redisClient *c) {
|
328
|
+
robj *set, *ele, *aux;
|
329
|
+
int64_t llele;
|
330
|
+
int encoding;
|
331
|
+
|
332
|
+
if ((set = lookupKeyWriteOrReply(c,c->argv[1],shared.nullbulk)) == NULL ||
|
333
|
+
checkType(c,set,REDIS_SET)) return;
|
334
|
+
|
335
|
+
encoding = setTypeRandomElement(set,&ele,&llele);
|
336
|
+
if (encoding == REDIS_ENCODING_INTSET) {
|
337
|
+
ele = createStringObjectFromLongLong(llele);
|
338
|
+
set->ptr = intsetRemove(set->ptr,llele,NULL);
|
339
|
+
} else {
|
340
|
+
incrRefCount(ele);
|
341
|
+
setTypeRemove(set,ele);
|
342
|
+
}
|
343
|
+
|
344
|
+
/* Replicate/AOF this command as an SREM operation */
|
345
|
+
aux = createStringObject("SREM",4);
|
346
|
+
rewriteClientCommandVector(c,3,aux,c->argv[1],ele);
|
347
|
+
decrRefCount(ele);
|
348
|
+
decrRefCount(aux);
|
349
|
+
|
350
|
+
addReplyBulk(c,ele);
|
351
|
+
if (setTypeSize(set) == 0) dbDelete(c->db,c->argv[1]);
|
352
|
+
touchWatchedKey(c->db,c->argv[1]);
|
353
|
+
server.dirty++;
|
354
|
+
}
|
355
|
+
|
356
|
+
void srandmemberCommand(redisClient *c) {
|
357
|
+
robj *set, *ele;
|
358
|
+
int64_t llele;
|
359
|
+
int encoding;
|
360
|
+
|
361
|
+
if ((set = lookupKeyReadOrReply(c,c->argv[1],shared.nullbulk)) == NULL ||
|
362
|
+
checkType(c,set,REDIS_SET)) return;
|
363
|
+
|
364
|
+
encoding = setTypeRandomElement(set,&ele,&llele);
|
365
|
+
if (encoding == REDIS_ENCODING_INTSET) {
|
366
|
+
addReplyBulkLongLong(c,llele);
|
367
|
+
} else {
|
368
|
+
addReplyBulk(c,ele);
|
369
|
+
}
|
370
|
+
}
|
371
|
+
|
372
|
+
int qsortCompareSetsByCardinality(const void *s1, const void *s2) {
|
373
|
+
return setTypeSize(*(robj**)s1)-setTypeSize(*(robj**)s2);
|
374
|
+
}
|
375
|
+
|
376
|
+
void sinterGenericCommand(redisClient *c, robj **setkeys, unsigned long setnum, robj *dstkey) {
|
377
|
+
robj **sets = zmalloc(sizeof(robj*)*setnum);
|
378
|
+
setTypeIterator *si;
|
379
|
+
robj *eleobj, *dstset = NULL;
|
380
|
+
int64_t intobj;
|
381
|
+
void *replylen = NULL;
|
382
|
+
unsigned long j, cardinality = 0;
|
383
|
+
int encoding;
|
384
|
+
|
385
|
+
for (j = 0; j < setnum; j++) {
|
386
|
+
robj *setobj = dstkey ?
|
387
|
+
lookupKeyWrite(c->db,setkeys[j]) :
|
388
|
+
lookupKeyRead(c->db,setkeys[j]);
|
389
|
+
if (!setobj) {
|
390
|
+
zfree(sets);
|
391
|
+
if (dstkey) {
|
392
|
+
if (dbDelete(c->db,dstkey)) {
|
393
|
+
touchWatchedKey(c->db,dstkey);
|
394
|
+
server.dirty++;
|
395
|
+
}
|
396
|
+
addReply(c,shared.czero);
|
397
|
+
} else {
|
398
|
+
addReply(c,shared.emptymultibulk);
|
399
|
+
}
|
400
|
+
return;
|
401
|
+
}
|
402
|
+
if (checkType(c,setobj,REDIS_SET)) {
|
403
|
+
zfree(sets);
|
404
|
+
return;
|
405
|
+
}
|
406
|
+
sets[j] = setobj;
|
407
|
+
}
|
408
|
+
/* Sort sets from the smallest to largest, this will improve our
|
409
|
+
* algorithm's performace */
|
410
|
+
qsort(sets,setnum,sizeof(robj*),qsortCompareSetsByCardinality);
|
411
|
+
|
412
|
+
/* The first thing we should output is the total number of elements...
|
413
|
+
* since this is a multi-bulk write, but at this stage we don't know
|
414
|
+
* the intersection set size, so we use a trick, append an empty object
|
415
|
+
* to the output list and save the pointer to later modify it with the
|
416
|
+
* right length */
|
417
|
+
if (!dstkey) {
|
418
|
+
replylen = addDeferredMultiBulkLength(c);
|
419
|
+
} else {
|
420
|
+
/* If we have a target key where to store the resulting set
|
421
|
+
* create this key with an empty set inside */
|
422
|
+
dstset = createIntsetObject();
|
423
|
+
}
|
424
|
+
|
425
|
+
/* Iterate all the elements of the first (smallest) set, and test
|
426
|
+
* the element against all the other sets, if at least one set does
|
427
|
+
* not include the element it is discarded */
|
428
|
+
si = setTypeInitIterator(sets[0]);
|
429
|
+
while((encoding = setTypeNext(si,&eleobj,&intobj)) != -1) {
|
430
|
+
for (j = 1; j < setnum; j++) {
|
431
|
+
if (sets[j] == sets[0]) continue;
|
432
|
+
if (encoding == REDIS_ENCODING_INTSET) {
|
433
|
+
/* intset with intset is simple... and fast */
|
434
|
+
if (sets[j]->encoding == REDIS_ENCODING_INTSET &&
|
435
|
+
!intsetFind((intset*)sets[j]->ptr,intobj))
|
436
|
+
{
|
437
|
+
break;
|
438
|
+
/* in order to compare an integer with an object we
|
439
|
+
* have to use the generic function, creating an object
|
440
|
+
* for this */
|
441
|
+
} else if (sets[j]->encoding == REDIS_ENCODING_HT) {
|
442
|
+
eleobj = createStringObjectFromLongLong(intobj);
|
443
|
+
if (!setTypeIsMember(sets[j],eleobj)) {
|
444
|
+
decrRefCount(eleobj);
|
445
|
+
break;
|
446
|
+
}
|
447
|
+
decrRefCount(eleobj);
|
448
|
+
}
|
449
|
+
} else if (encoding == REDIS_ENCODING_HT) {
|
450
|
+
/* Optimization... if the source object is integer
|
451
|
+
* encoded AND the target set is an intset, we can get
|
452
|
+
* a much faster path. */
|
453
|
+
if (eleobj->encoding == REDIS_ENCODING_INT &&
|
454
|
+
sets[j]->encoding == REDIS_ENCODING_INTSET &&
|
455
|
+
!intsetFind((intset*)sets[j]->ptr,(long)eleobj->ptr))
|
456
|
+
{
|
457
|
+
break;
|
458
|
+
/* else... object to object check is easy as we use the
|
459
|
+
* type agnostic API here. */
|
460
|
+
} else if (!setTypeIsMember(sets[j],eleobj)) {
|
461
|
+
break;
|
462
|
+
}
|
463
|
+
}
|
464
|
+
}
|
465
|
+
|
466
|
+
/* Only take action when all sets contain the member */
|
467
|
+
if (j == setnum) {
|
468
|
+
if (!dstkey) {
|
469
|
+
if (encoding == REDIS_ENCODING_HT)
|
470
|
+
addReplyBulk(c,eleobj);
|
471
|
+
else
|
472
|
+
addReplyBulkLongLong(c,intobj);
|
473
|
+
cardinality++;
|
474
|
+
} else {
|
475
|
+
if (encoding == REDIS_ENCODING_INTSET) {
|
476
|
+
eleobj = createStringObjectFromLongLong(intobj);
|
477
|
+
setTypeAdd(dstset,eleobj);
|
478
|
+
decrRefCount(eleobj);
|
479
|
+
} else {
|
480
|
+
setTypeAdd(dstset,eleobj);
|
481
|
+
}
|
482
|
+
}
|
483
|
+
}
|
484
|
+
}
|
485
|
+
setTypeReleaseIterator(si);
|
486
|
+
|
487
|
+
if (dstkey) {
|
488
|
+
/* Store the resulting set into the target, if the intersection
|
489
|
+
* is not an empty set. */
|
490
|
+
dbDelete(c->db,dstkey);
|
491
|
+
if (setTypeSize(dstset) > 0) {
|
492
|
+
dbAdd(c->db,dstkey,dstset);
|
493
|
+
addReplyLongLong(c,setTypeSize(dstset));
|
494
|
+
} else {
|
495
|
+
decrRefCount(dstset);
|
496
|
+
addReply(c,shared.czero);
|
497
|
+
}
|
498
|
+
touchWatchedKey(c->db,dstkey);
|
499
|
+
server.dirty++;
|
500
|
+
} else {
|
501
|
+
setDeferredMultiBulkLength(c,replylen,cardinality);
|
502
|
+
}
|
503
|
+
zfree(sets);
|
504
|
+
}
|
505
|
+
|
506
|
+
void sinterCommand(redisClient *c) {
|
507
|
+
sinterGenericCommand(c,c->argv+1,c->argc-1,NULL);
|
508
|
+
}
|
509
|
+
|
510
|
+
void sinterstoreCommand(redisClient *c) {
|
511
|
+
sinterGenericCommand(c,c->argv+2,c->argc-2,c->argv[1]);
|
512
|
+
}
|
513
|
+
|
514
|
+
#define REDIS_OP_UNION 0
|
515
|
+
#define REDIS_OP_DIFF 1
|
516
|
+
#define REDIS_OP_INTER 2
|
517
|
+
|
518
|
+
void sunionDiffGenericCommand(redisClient *c, robj **setkeys, int setnum, robj *dstkey, int op) {
|
519
|
+
robj **sets = zmalloc(sizeof(robj*)*setnum);
|
520
|
+
setTypeIterator *si;
|
521
|
+
robj *ele, *dstset = NULL;
|
522
|
+
int j, cardinality = 0;
|
523
|
+
|
524
|
+
for (j = 0; j < setnum; j++) {
|
525
|
+
robj *setobj = dstkey ?
|
526
|
+
lookupKeyWrite(c->db,setkeys[j]) :
|
527
|
+
lookupKeyRead(c->db,setkeys[j]);
|
528
|
+
if (!setobj) {
|
529
|
+
sets[j] = NULL;
|
530
|
+
continue;
|
531
|
+
}
|
532
|
+
if (checkType(c,setobj,REDIS_SET)) {
|
533
|
+
zfree(sets);
|
534
|
+
return;
|
535
|
+
}
|
536
|
+
sets[j] = setobj;
|
537
|
+
}
|
538
|
+
|
539
|
+
/* We need a temp set object to store our union. If the dstkey
|
540
|
+
* is not NULL (that is, we are inside an SUNIONSTORE operation) then
|
541
|
+
* this set object will be the resulting object to set into the target key*/
|
542
|
+
dstset = createIntsetObject();
|
543
|
+
|
544
|
+
/* Iterate all the elements of all the sets, add every element a single
|
545
|
+
* time to the result set */
|
546
|
+
for (j = 0; j < setnum; j++) {
|
547
|
+
if (op == REDIS_OP_DIFF && j == 0 && !sets[j]) break; /* result set is empty */
|
548
|
+
if (!sets[j]) continue; /* non existing keys are like empty sets */
|
549
|
+
|
550
|
+
si = setTypeInitIterator(sets[j]);
|
551
|
+
while((ele = setTypeNextObject(si)) != NULL) {
|
552
|
+
if (op == REDIS_OP_UNION || j == 0) {
|
553
|
+
if (setTypeAdd(dstset,ele)) {
|
554
|
+
cardinality++;
|
555
|
+
}
|
556
|
+
} else if (op == REDIS_OP_DIFF) {
|
557
|
+
if (setTypeRemove(dstset,ele)) {
|
558
|
+
cardinality--;
|
559
|
+
}
|
560
|
+
}
|
561
|
+
decrRefCount(ele);
|
562
|
+
}
|
563
|
+
setTypeReleaseIterator(si);
|
564
|
+
|
565
|
+
/* Exit when result set is empty. */
|
566
|
+
if (op == REDIS_OP_DIFF && cardinality == 0) break;
|
567
|
+
}
|
568
|
+
|
569
|
+
/* Output the content of the resulting set, if not in STORE mode */
|
570
|
+
if (!dstkey) {
|
571
|
+
addReplyMultiBulkLen(c,cardinality);
|
572
|
+
si = setTypeInitIterator(dstset);
|
573
|
+
while((ele = setTypeNextObject(si)) != NULL) {
|
574
|
+
addReplyBulk(c,ele);
|
575
|
+
decrRefCount(ele);
|
576
|
+
}
|
577
|
+
setTypeReleaseIterator(si);
|
578
|
+
decrRefCount(dstset);
|
579
|
+
} else {
|
580
|
+
/* If we have a target key where to store the resulting set
|
581
|
+
* create this key with the result set inside */
|
582
|
+
dbDelete(c->db,dstkey);
|
583
|
+
if (setTypeSize(dstset) > 0) {
|
584
|
+
dbAdd(c->db,dstkey,dstset);
|
585
|
+
addReplyLongLong(c,setTypeSize(dstset));
|
586
|
+
} else {
|
587
|
+
decrRefCount(dstset);
|
588
|
+
addReply(c,shared.czero);
|
589
|
+
}
|
590
|
+
touchWatchedKey(c->db,dstkey);
|
591
|
+
server.dirty++;
|
592
|
+
}
|
593
|
+
zfree(sets);
|
594
|
+
}
|
595
|
+
|
596
|
+
void sunionCommand(redisClient *c) {
|
597
|
+
sunionDiffGenericCommand(c,c->argv+1,c->argc-1,NULL,REDIS_OP_UNION);
|
598
|
+
}
|
599
|
+
|
600
|
+
void sunionstoreCommand(redisClient *c) {
|
601
|
+
sunionDiffGenericCommand(c,c->argv+2,c->argc-2,c->argv[1],REDIS_OP_UNION);
|
602
|
+
}
|
603
|
+
|
604
|
+
void sdiffCommand(redisClient *c) {
|
605
|
+
sunionDiffGenericCommand(c,c->argv+1,c->argc-1,NULL,REDIS_OP_DIFF);
|
606
|
+
}
|
607
|
+
|
608
|
+
void sdiffstoreCommand(redisClient *c) {
|
609
|
+
sunionDiffGenericCommand(c,c->argv+2,c->argc-2,c->argv[1],REDIS_OP_DIFF);
|
610
|
+
}
|