From: antirez Date: Tue, 13 Apr 2010 16:30:55 +0000 (+0200) Subject: Merge branch 'hmget' of git://github.com/pietern/redis X-Git-Url: https://git.saurik.com/redis.git/commitdiff_plain/b60bace9f945d30029596a6bd91412d2e1053c69?ds=sidebyside;hp=-c Merge branch 'hmget' of git://github.com/pietern/redis --- b60bace9f945d30029596a6bd91412d2e1053c69 diff --combined redis.c index cbb0993a,095a02e4..e78f75aa --- a/redis.c +++ b/redis.c @@@ -704,8 -704,9 +704,9 @@@ static void substrCommand(redisClient * static void zrankCommand(redisClient *c); static void zrevrankCommand(redisClient *c); static void hsetCommand(redisClient *c); - static void hmsetCommand(redisClient *c); static void hgetCommand(redisClient *c); + static void hmsetCommand(redisClient *c); + static void hmgetCommand(redisClient *c); static void hdelCommand(redisClient *c); static void hlenCommand(redisClient *c); static void zremrangebyrankCommand(redisClient *c); @@@ -781,9 -782,10 +782,10 @@@ static struct redisCommand cmdTable[] {"zrank",zrankCommand,3,REDIS_CMD_BULK,NULL,1,1,1}, {"zrevrank",zrevrankCommand,3,REDIS_CMD_BULK,NULL,1,1,1}, {"hset",hsetCommand,4,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,NULL,1,1,1}, + {"hget",hgetCommand,3,REDIS_CMD_BULK,NULL,1,1,1}, {"hmset",hmsetCommand,-4,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,NULL,1,1,1}, + {"hmget",hmgetCommand,-3,REDIS_CMD_BULK,NULL,1,1,1}, {"hincrby",hincrbyCommand,4,REDIS_CMD_INLINE|REDIS_CMD_DENYOOM,NULL,1,1,1}, - {"hget",hgetCommand,3,REDIS_CMD_BULK,NULL,1,1,1}, {"hdel",hdelCommand,3,REDIS_CMD_BULK,NULL,1,1,1}, {"hlen",hlenCommand,2,REDIS_CMD_INLINE,NULL,1,1,1}, {"hkeys",hkeysCommand,2,REDIS_CMD_INLINE,NULL,1,1,1}, @@@ -1981,7 -1983,7 +1983,7 @@@ static void sendReplyToClient(aeEventLo /* Use writev() if we have enough buffers to send */ if (!server.glueoutputbuf && - listLength(c->reply) > REDIS_WRITEV_THRESHOLD && + listLength(c->reply) > REDIS_WRITEV_THRESHOLD && !(c->flags & REDIS_MASTER)) { sendReplyToClientWritev(el, fd, privdata, mask); @@@ -2059,7 -2061,7 +2061,7 @@@ static void sendReplyToClientWritev(aeE o = listNodeValue(node); objlen = sdslen(o->ptr); - if (totwritten + objlen - offset > REDIS_MAX_WRITE_PER_EVENT) + if (totwritten + objlen - offset > REDIS_MAX_WRITE_PER_EVENT) break; if(ion == REDIS_WRITEV_IOVEC_COUNT) @@@ -2107,7 -2109,7 +2109,7 @@@ } } - if (totwritten > 0) + if (totwritten > 0) c->lastinteraction = time(NULL); if (listLength(c->reply) == 0) { @@@ -2417,7 -2419,7 +2419,7 @@@ again if (p) { sds query, *argv; int argc, j; - + query = c->querybuf; c->querybuf = sdsempty(); querylen = 1+(p-(query)); @@@ -2959,7 -2961,7 +2961,7 @@@ static int isStringRepresentableAsLong( char buf[32], *endptr; long value; int slen; - + value = strtol(s, &endptr, 10); if (endptr[0] != '\0') return REDIS_ERR; slen = snprintf(buf,32,"%ld",value); @@@ -3007,7 -3009,7 +3009,7 @@@ static robj *tryObjectEncoding(robj *o * If the object is already raw-encoded just increment the ref count. */ static robj *getDecodedObject(robj *o) { robj *dec; - + if (o->encoding == REDIS_ENCODING_RAW) { incrRefCount(o); return o; @@@ -3065,77 -3067,6 +3067,77 @@@ static size_t stringObjectLen(robj *o) } } +static int getDoubleFromObject(redisClient *c, robj *o, double *value) { + double parsedValue; + char *eptr = NULL; + + if (o && o->type != REDIS_STRING) { + addReplySds(c,sdsnew("-ERR value is not a double\r\n")); + return REDIS_ERR; + } + + if (o == NULL) + parsedValue = 0; + else if (o->encoding == REDIS_ENCODING_RAW) + parsedValue = strtod(o->ptr, &eptr); + else if (o->encoding == REDIS_ENCODING_INT) + parsedValue = (long)o->ptr; + else + redisAssert(1 != 1); + + if (eptr != NULL && *eptr != '\0') { + addReplySds(c,sdsnew("-ERR value is not a double\r\n")); + return REDIS_ERR; + } + + *value = parsedValue; + + return REDIS_OK; +} + +static int getLongLongFromObject(redisClient *c, robj *o, long long *value) { + long long parsedValue; + char *eptr = NULL; + + if (o && o->type != REDIS_STRING) { + addReplySds(c,sdsnew("-ERR value is not an integer\r\n")); + return REDIS_ERR; + } + + if (o == NULL) + parsedValue = 0; + else if (o->encoding == REDIS_ENCODING_RAW) + parsedValue = strtoll(o->ptr, &eptr, 10); + else if (o->encoding == REDIS_ENCODING_INT) + parsedValue = (long)o->ptr; + else + redisAssert(1 != 1); + + if (eptr != NULL && *eptr != '\0') { + addReplySds(c,sdsnew("-ERR value is not an integer\r\n")); + return REDIS_ERR; + } + + *value = parsedValue; + + return REDIS_OK; +} + +static int getLongFromObject(redisClient *c, robj *o, long *value) { + long long actualValue; + + if (getLongLongFromObject(c, o, &actualValue) != REDIS_OK) return REDIS_ERR; + + if (actualValue < LONG_MIN || actualValue > LONG_MAX) { + addReplySds(c,sdsnew("-ERR value is out of range\r\n")); + return REDIS_ERR; + } + + *value = actualValue; + + return REDIS_OK; +} + /*============================ RDB saving/loading =========================== */ static int rdbSaveType(FILE *fp, unsigned char type) { @@@ -3408,7 -3339,7 +3410,7 @@@ static off_t rdbSavedObjectLen(robj *o /* Return the number of pages required to save this object in the swap file */ static off_t rdbSavedObjectPages(robj *o, FILE *fp) { off_t bytes = rdbSavedObjectLen(o,fp); - + return (bytes+(server.vm_page_size-1))/server.vm_page_size; } @@@ -3491,7 -3422,7 +3493,7 @@@ static int rdbSave(char *filename) fflush(fp); fsync(fileno(fp)); fclose(fp); - + /* Use RENAME to make sure the DB file is changed atomically only * if the generate DB file is ok. */ if (rename(tmpfile,filename) == -1) { @@@ -3921,7 -3852,7 +3923,7 @@@ static void setnxCommand(redisClient *c static int getGenericCommand(redisClient *c) { robj *o; - + if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.nullbulk)) == NULL) return REDIS_OK; @@@ -3952,7 -3883,7 +3954,7 @@@ static void getsetCommand(redisClient * static void mgetCommand(redisClient *c) { int j; - + addReplySds(c,sdscatprintf(sdsempty(),"*%d\r\n",c->argc-1)); for (j = 1; j < c->argc; j++) { robj *o = lookupKeyRead(c->db,c->argv[j]); @@@ -4019,10 -3950,24 +4021,10 @@@ static void incrDecrCommand(redisClien long long value; int retval; robj *o; - + o = lookupKeyWrite(c->db,c->argv[1]); - if (o == NULL) { - value = 0; - } else { - if (o->type != REDIS_STRING) { - value = 0; - } else { - char *eptr; - if (o->encoding == REDIS_ENCODING_RAW) - value = strtoll(o->ptr, &eptr, 10); - else if (o->encoding == REDIS_ENCODING_INT) - value = (long)o->ptr; - else - redisAssert(1 != 1); - } - } + if (getLongLongFromObject(c, o, &value) != REDIS_OK) return; value += incr; o = createObject(REDIS_STRING,sdscatprintf(sdsempty(),"%lld",value)); @@@ -4049,18 -3994,12 +4051,18 @@@ static void decrCommand(redisClient *c } static void incrbyCommand(redisClient *c) { - long long incr = strtoll(c->argv[2]->ptr, NULL, 10); + long long incr; + + if (getLongLongFromObject(c, c->argv[2], &incr) != REDIS_OK) return; + incrDecrCommand(c,incr); } static void decrbyCommand(redisClient *c) { - long long incr = strtoll(c->argv[2]->ptr, NULL, 10); + long long incr; + + if (getLongLongFromObject(c, c->argv[2], &incr) != REDIS_OK) return; + incrDecrCommand(c,-incr); } @@@ -4078,7 -4017,7 +4080,7 @@@ static void appendCommand(redisClient * totlen = stringObjectLen(c->argv[2]); } else { dictEntry *de; - + de = dictFind(c->db->dict,c->argv[1]); assert(de != NULL); @@@ -4167,7 -4106,7 +4169,7 @@@ static void existsCommand(redisClient * static void selectCommand(redisClient *c) { int id = atoi(c->argv[1]->ptr); - + if (selectDb(c,id) == REDIS_ERR) { addReplySds(c,sdsnew("-ERR invalid DB index\r\n")); } else { @@@ -4177,7 -4116,7 +4179,7 @@@ static void randomkeyCommand(redisClient *c) { dictEntry *de; - + while(1) { de = dictGetRandomKey(c->db->dict); if (!de || expireIfNeeded(c->db,dictGetEntryKey(de)) == 0) break; @@@ -4305,7 -4244,7 +4307,7 @@@ static void shutdownCommand(redisClien * in the next cron() Redis will be notified that the background * saving aborted, handling special stuff like slaves pending for * synchronization... */ - redisLog(REDIS_WARNING,"Error trying to save the DB, can't exit"); + redisLog(REDIS_WARNING,"Error trying to save the DB, can't exit"); addReplySds(c, sdsnew("-ERR can't quit, problems saving the DB\r\n")); } @@@ -4449,7 -4388,7 +4451,7 @@@ static void llenCommand(redisClient *c if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL || checkType(c,o,REDIS_LIST)) return; - + l = o->ptr; addReplyUlong(c,listLength(l)); } @@@ -4540,8 -4479,8 +4542,8 @@@ static void lrangeCommand(redisClient * listNode *ln; robj *ele; - if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.nullmultibulk)) == NULL || - checkType(c,o,REDIS_LIST)) return; + if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.emptymultibulk)) == NULL + || checkType(c,o,REDIS_LIST)) return; list = o->ptr; llen = listLength(list); @@@ -4805,7 -4744,7 +4807,7 @@@ static void scardCommand(redisClient *c if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL || checkType(c,o,REDIS_SET)) return; - + s = o->ptr; addReplyUlong(c,dictSize(s)); } @@@ -4874,7 -4813,7 +4876,7 @@@ static void sinterGenericCommand(redisC server.dirty++; addReply(c,shared.czero); } else { - addReply(c,shared.nullmultibulk); + addReply(c,shared.emptymultibulk); } return; } @@@ -5096,7 -5035,7 +5098,7 @@@ static zskiplistNode *zslCreateNode(in static zskiplist *zslCreate(void) { int j; zskiplist *zsl; - + zsl = zmalloc(sizeof(*zsl)); zsl->level = 1; zsl->length = 0; @@@ -5429,7 -5368,7 +5431,7 @@@ static void zaddGenericCommand(redisCli } else { dictEntry *de; double *oldscore; - + /* case 2: Score update operation */ de = dictFind(zs->dict,ele); redisAssert(de != NULL); @@@ -5458,16 -5397,14 +5460,16 @@@ static void zaddCommand(redisClient *c) { double scoreval; - scoreval = strtod(c->argv[2]->ptr,NULL); + if (getDoubleFromObject(c, c->argv[2], &scoreval) != REDIS_OK) return; + zaddGenericCommand(c,c->argv[1],c->argv[3],scoreval,0); } static void zincrbyCommand(redisClient *c) { double scoreval; - scoreval = strtod(c->argv[2]->ptr,NULL); + if (getDoubleFromObject(c, c->argv[2], &scoreval) != REDIS_OK) return; + zaddGenericCommand(c,c->argv[1],c->argv[3],scoreval,1); } @@@ -5501,15 -5438,12 +5503,15 @@@ static void zremCommand(redisClient *c } static void zremrangebyscoreCommand(redisClient *c) { - double min = strtod(c->argv[2]->ptr,NULL); - double max = strtod(c->argv[3]->ptr,NULL); + double min; + double max; long deleted; robj *zsetobj; zset *zs; + if ((getDoubleFromObject(c, c->argv[2], &min) != REDIS_OK) || + (getDoubleFromObject(c, c->argv[3], &max) != REDIS_OK)) return; + if ((zsetobj = lookupKeyWriteOrReply(c,c->argv[1],shared.czero)) == NULL || checkType(c,zsetobj,REDIS_ZSET)) return; @@@ -5522,16 -5456,13 +5524,16 @@@ } static void zremrangebyrankCommand(redisClient *c) { - int start = atoi(c->argv[2]->ptr); - int end = atoi(c->argv[3]->ptr); + long start; + long end; int llen; long deleted; robj *zsetobj; zset *zs; + if ((getLongFromObject(c, c->argv[2], &start) != REDIS_OK) || + (getLongFromObject(c, c->argv[3], &end) != REDIS_OK)) return; + if ((zsetobj = lookupKeyWriteOrReply(c,c->argv[1],shared.czero)) == NULL || checkType(c,zsetobj,REDIS_ZSET)) return; zs = zsetobj->ptr; @@@ -5638,8 -5569,7 +5640,8 @@@ static void zunionInterGenericCommand(r if (remaining >= (zsetnum + 1) && !strcasecmp(c->argv[j]->ptr,"weights")) { j++; remaining--; for (i = 0; i < zsetnum; i++, j++, remaining--) { - src[i].weight = strtod(c->argv[j]->ptr, NULL); + if (getDoubleFromObject(c, c->argv[j], &src[i].weight) != REDIS_OK) + return; } } else if (remaining >= 2 && !strcasecmp(c->argv[j]->ptr,"aggregate")) { j++; remaining--; @@@ -5761,8 -5691,8 +5763,8 @@@ static void zinterCommand(redisClient * static void zrangeGenericCommand(redisClient *c, int reverse) { robj *o; - int start = atoi(c->argv[2]->ptr); - int end = atoi(c->argv[3]->ptr); + long start; + long end; int withscores = 0; int llen; int rangelen, j; @@@ -5771,9 -5701,6 +5773,9 @@@ zskiplistNode *ln; robj *ele; + if ((getLongFromObject(c, c->argv[2], &start) != REDIS_OK) || + (getLongFromObject(c, c->argv[3], &end) != REDIS_OK)) return; + if (c->argc == 5 && !strcasecmp(c->argv[4]->ptr,"withscores")) { withscores = 1; } else if (c->argc >= 5) { @@@ -5781,8 -5708,8 +5783,8 @@@ return; } - if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.nullmultibulk)) == NULL || - checkType(c,o,REDIS_ZSET)) return; + if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.emptymultibulk)) == NULL + || checkType(c,o,REDIS_ZSET)) return; zsetobj = o->ptr; zsl = zsetobj->zsl; llen = zsl->length; @@@ -5888,7 -5815,7 +5890,7 @@@ static void genericZrangebyscoreCommand /* Ok, lookup the key and get the range */ o = lookupKeyRead(c->db,c->argv[1]); if (o == NULL) { - addReply(c,justcount ? shared.czero : shared.nullmultibulk); + addReply(c,justcount ? shared.czero : shared.emptymultibulk); } else { if (o->type != REDIS_ZSET) { addReply(c,shared.wrongtypeerr); @@@ -6160,8 -6087,7 +6162,8 @@@ static void hincrbyCommand(redisClient } } - incr = strtoll(c->argv[3]->ptr, NULL, 10); + if (getLongLongFromObject(c, c->argv[3], &incr) != REDIS_OK) return; + if (o->encoding == REDIS_ENCODING_ZIPMAP) { unsigned char *zm = o->ptr; unsigned char *zval; @@@ -6253,6 -6179,55 +6255,55 @@@ static void hgetCommand(redisClient *c } } + static void hmgetCommand(redisClient *c) { + int i; + + robj *o = lookupKeyRead(c->db, c->argv[1]); + if (o == NULL) { + addReplySds(c,sdscatprintf(sdsempty(),"*%d\r\n",c->argc-2)); + for (i = 2; i < c->argc; i++) { + addReply(c,shared.nullbulk); + } + return; + } else { + if (o->type != REDIS_HASH) { + addReply(c,shared.wrongtypeerr); + return; + } + } + + addReplySds(c,sdscatprintf(sdsempty(),"*%d\r\n",c->argc-2)); + if (o->encoding == REDIS_ENCODING_ZIPMAP) { + unsigned char *zm = o->ptr; + unsigned char *v; + unsigned int vlen; + robj *field; + + for (i = 2; i < c->argc; i++) { + field = getDecodedObject(c->argv[i]); + if (zipmapGet(zm,field->ptr,sdslen(field->ptr),&v,&vlen)) { + addReplySds(c,sdscatprintf(sdsempty(),"$%u\r\n", vlen)); + addReplySds(c,sdsnewlen(v,vlen)); + addReply(c,shared.crlf); + } else { + addReply(c,shared.nullbulk); + } + decrRefCount(field); + } + } else { + dictEntry *de; + + for (i = 2; i < c->argc; i++) { + de = dictFind(o->ptr,c->argv[i]); + if (de != NULL) { + addReplyBulk(c,(robj*)dictGetEntryVal(de)); + } else { + addReply(c,shared.nullbulk); + } + } + } + } + static void hdelCommand(redisClient *c) { robj *o; int deleted = 0; @@@ -6296,7 -6271,7 +6347,7 @@@ static void genericHgetallCommand(redis robj *o, *lenobj; unsigned long count = 0; - if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.nullmultibulk)) == NULL + if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.emptymultibulk)) == NULL || checkType(c,o,REDIS_HASH)) return; lenobj = createObject(REDIS_STRING,NULL); @@@ -6536,7 -6511,7 +6587,7 @@@ static void sortCommand(redisClient *c /* Lookup the key to sort. It must be of the right types */ sortval = lookupKeyRead(c->db,c->argv[1]); if (sortval == NULL) { - addReply(c,shared.nullmultibulk); + addReply(c,shared.emptymultibulk); return; } if (sortval->type != REDIS_SET && sortval->type != REDIS_LIST && @@@ -6993,13 -6968,8 +7044,13 @@@ static int deleteIfVolatile(redisDb *db return dictDelete(db->dict,key) == DICT_OK; } -static void expireGenericCommand(redisClient *c, robj *key, time_t seconds) { +static void expireGenericCommand(redisClient *c, robj *key, robj *param, long offset) { dictEntry *de; + time_t seconds; + + if (getLongFromObject(c, param, &seconds) != REDIS_OK) return; + + seconds -= offset; de = dictFind(c->db->dict,key); if (de == NULL) { @@@ -7023,11 -6993,11 +7074,11 @@@ } static void expireCommand(redisClient *c) { - expireGenericCommand(c,c->argv[1],strtol(c->argv[2]->ptr,NULL,10)); + expireGenericCommand(c,c->argv[1],c->argv[2],0); } static void expireatCommand(redisClient *c) { - expireGenericCommand(c,c->argv[1],strtol(c->argv[2]->ptr,NULL,10)-time(NULL)); + expireGenericCommand(c,c->argv[1],c->argv[2],time(NULL)); } static void ttlCommand(redisClient *c) { @@@ -7271,7 -7241,7 +7322,7 @@@ static void blockingPopGenericCommand(r * non-blocking POP operation */ robj *argv[2], **orig_argv; int orig_argc; - + /* We need to alter the command arguments before to call * popGenericCommand() as the command takes a single key. */ orig_argv = c->argv; @@@ -7508,7 -7478,7 +7559,7 @@@ static void updateSlavesWaitingBgsave(i slave->replstate = REDIS_REPL_WAIT_BGSAVE_END; } else if (slave->replstate == REDIS_REPL_WAIT_BGSAVE_END) { struct redis_stat buf; - + if (bgsaveerr != REDIS_OK) { freeClient(slave); redisLog(REDIS_WARNING,"SYNC failed. BGSAVE child returned an error"); @@@ -8165,7 -8135,7 +8216,7 @@@ static int rewriteAppendOnlyFile(char * fflush(fp); fsync(fileno(fp)); fclose(fp); - + /* Use RENAME to make sure the DB file is changed atomically only * if the generate DB file is ok. */ if (rename(tmpfile,filename) == -1) { @@@ -8283,7 -8253,7 +8334,7 @@@ static void aofRemoveTempFile(pid_t chi static void expandVmSwapFilename(void) { char *p = strstr(server.vm_swap_file,"%p"); sds new; - + if (!p) return; new = sdsempty(); *p = '\0'; @@@ -8408,7 -8378,7 +8459,7 @@@ static int vmFreePage(off_t page) } /* Find N contiguous free pages storing the first page of the cluster in *first. - * Returns REDIS_OK if it was able to find N contiguous pages, otherwise + * Returns REDIS_OK if it was able to find N contiguous pages, otherwise * REDIS_ERR is returned. * * This function uses a simple algorithm: we try to allocate @@@ -8419,7 -8389,7 +8470,7 @@@ * we try to find less populated places doing a forward jump of * REDIS_VM_MAX_RANDOM_JUMP, then we start scanning again a few pages * without hurry, and then we jump again and so forth... - * + * * This function can be improved using a free list to avoid to guess * too much, since we could collect data about freed pages. * @@@ -9073,7 -9043,7 +9124,7 @@@ static void *IOThreadEntryPoint(void *a listDelNode(server.io_processing,ln); listAddNodeTail(server.io_processed,j); unlockThreadedIO(); - + /* Signal the main thread there is new stuff to process */ assert(write(server.io_ready_pipe_write,"x",1) == 1); } @@@ -9151,7 -9121,7 +9202,7 @@@ static void queueIOJob(iojob *j) static int vmSwapObjectThreaded(robj *key, robj *val, redisDb *db) { iojob *j; - + assert(key->storage == REDIS_VM_MEMORY); assert(key->refcount == 1); @@@ -9195,7 -9165,7 +9246,7 @@@ static int waitForSwappedKey(redisClien vmCancelThreadedIOJob(o); return 0; } - + /* OK: the key is either swapped, or being loaded just now. */ /* Add the key to the list of keys this client is waiting for. @@@ -9908,7 -9878,7 +9959,7 @@@ static void *getMcontextEip(ucontext_t return (void*) uc->uc_mcontext->__ss.__rip; #else return (void*) uc->uc_mcontext->__ss.__eip; - #endif + #endif #elif defined(__i386__) || defined(__X86_64__) || defined(__x86_64__) return (void*) uc->uc_mcontext.gregs[REG_EIP]; /* Linux 32/64 bit */ #elif defined(__ia64__) /* Linux IA64 */ @@@ -9933,7 -9903,7 +9984,7 @@@ static void segvHandler(int sig, siginf redisLog(REDIS_WARNING, "%s",infostring); /* It's not safe to sdsfree() the returned string under memory * corruption conditions. Let it leak as we are going to abort */ - + trace_size = backtrace(trace, 100); /* overwrite sigaction with caller's address */ if (getMcontextEip(uc) != NULL) { diff --combined test-redis.tcl index f45c4738,6ebcb039..f321258a --- a/test-redis.tcl +++ b/test-redis.tcl @@@ -234,7 -234,7 +234,7 @@@ proc main {server port} # The following AUTH test should be enabled only when requirepass # is set in redis.conf and redis-server was started with - # redis.conf as the first argument. + # redis.conf as the first argument. #test {AUTH with requirepass in redis.conf} { # $r auth foobared @@@ -359,18 -359,10 +359,18 @@@ $r incrby novar 17179869184 } {34359738368} - test {INCR against key with spaces (no integer encoded)} { + test {INCR fails against key with spaces (no integer encoded)} { $r set novar " 11 " - $r incr novar - } {12} + catch {$r incr novar} err + format $err + } {ERR*} + + test {INCR fails against a key holding a list} { + $r rpush mylist 1 + catch {$r incr novar} err + $r rpop mylist + format $err + } {ERR*} test {DECRBY over 32bit value with over 32bit increment, negative res} { $r set novar 17179869184 @@@ -839,7 -831,7 +839,7 @@@ test {SUNION with two sets} { lsort [$r sunion set1 set2] } [lsort -uniq "[$r smembers set1] [$r smembers set2]"] - + test {SINTERSTORE with two sets} { $r sinterstore setres set1 set2 lsort [$r smembers setres] @@@ -910,9 -902,9 +910,9 @@@ $r lpush mysavelist world $r set myemptykey {} $r set mynormalkey {blablablba} - $r zadd mytestzset a 10 - $r zadd mytestzset b 20 - $r zadd mytestzset c 30 + $r zadd mytestzset 10 a + $r zadd mytestzset 20 b + $r zadd mytestzset 30 c $r save } {OK} @@@ -928,7 -920,7 +928,7 @@@ } lsort [array names myset] } {a b c} - + test {Create a random list and a random set} { set tosort {} array set seenrand {} @@@ -1671,6 -1663,46 +1671,46 @@@ $r hmset bighash {*}$args } {OK} + test {HMGET against non existing key and fields} { + set rv {} + lappend rv [$r hmget doesntexist __123123123__ __456456456__] + lappend rv [$r hmget smallhash __123123123__ __456456456__] + lappend rv [$r hmget bighash __123123123__ __456456456__] + set _ $rv + } {{{} {}} {{} {}} {{} {}}} + + test {HMGET - small hash} { + set keys {} + set vals {} + foreach {k v} [array get smallhash] { + lappend keys $k + lappend vals $v + } + set err {} + set result [$r hmget smallhash {*}$keys] + if {$vals ne $result} { + set err "$vals != $result" + break + } + set _ $err + } {} + + test {HMGET - big hash} { + set keys {} + set vals {} + foreach {k v} [array get bighash] { + lappend keys $k + lappend vals $v + } + set err {} + set result [$r hmget bighash {*}$keys] + if {$vals ne $result} { + set err "$vals != $result" + break + } + set _ $err + } {} + test {HKEYS - small hash} { lsort [$r hkeys smallhash] } [lsort [array names smallhash *]]