Merge branch 'unstable' of https://github.com/antirez/redis into unstable
Former-commit-id: be3cb1ad3386f382ed7506dbfd1adb810e327007
This commit is contained in:
commit
aa76a89a9a
@ -1103,8 +1103,8 @@ void configSetCommand(client *c) {
|
|||||||
int soft_seconds;
|
int soft_seconds;
|
||||||
|
|
||||||
class = getClientTypeByName(v[j]);
|
class = getClientTypeByName(v[j]);
|
||||||
hard = strtoll(v[j+1],NULL,10);
|
hard = memtoll(v[j+1],NULL);
|
||||||
soft = strtoll(v[j+2],NULL,10);
|
soft = memtoll(v[j+2],NULL);
|
||||||
soft_seconds = strtoll(v[j+3],NULL,10);
|
soft_seconds = strtoll(v[j+3],NULL,10);
|
||||||
|
|
||||||
server.client_obuf_limits[class].hard_limit_bytes = hard;
|
server.client_obuf_limits[class].hard_limit_bytes = hard;
|
||||||
|
@ -659,7 +659,7 @@ void georadiusGeneric(client *c, int flags) {
|
|||||||
zsetConvertToZiplistIfNeeded(zobj,maxelelen);
|
zsetConvertToZiplistIfNeeded(zobj,maxelelen);
|
||||||
setKey(c->db,storekey,zobj);
|
setKey(c->db,storekey,zobj);
|
||||||
decrRefCount(zobj);
|
decrRefCount(zobj);
|
||||||
notifyKeyspaceEvent(NOTIFY_LIST,"georadiusstore",storekey,
|
notifyKeyspaceEvent(NOTIFY_ZSET,"georadiusstore",storekey,
|
||||||
c->db->id);
|
c->db->id);
|
||||||
server.dirty += returned_items;
|
server.dirty += returned_items;
|
||||||
} else if (dbDelete(c->db,storekey)) {
|
} else if (dbDelete(c->db,storekey)) {
|
||||||
|
168
src/module.c
168
src/module.c
@ -47,9 +47,21 @@ struct RedisModule {
|
|||||||
int ver; /* Module version. We use just progressive integers. */
|
int ver; /* Module version. We use just progressive integers. */
|
||||||
int apiver; /* Module API version as requested during initialization.*/
|
int apiver; /* Module API version as requested during initialization.*/
|
||||||
list *types; /* Module data types. */
|
list *types; /* Module data types. */
|
||||||
|
list *usedby; /* List of modules using APIs from this one. */
|
||||||
|
list *using; /* List of modules we use some APIs of. */
|
||||||
};
|
};
|
||||||
typedef struct RedisModule RedisModule;
|
typedef struct RedisModule RedisModule;
|
||||||
|
|
||||||
|
/* This represents a shared API. Shared APIs will be used to populate
|
||||||
|
* the server.sharedapi dictionary, mapping names of APIs exported by
|
||||||
|
* modules for other modules to use, to their structure specifying the
|
||||||
|
* function pointer that can be called. */
|
||||||
|
struct RedisModuleSharedAPI {
|
||||||
|
void *func;
|
||||||
|
RedisModule *module;
|
||||||
|
};
|
||||||
|
typedef struct RedisModuleSharedAPI RedisModuleSharedAPI;
|
||||||
|
|
||||||
static dict *modules; /* Hash table of modules. SDS -> RedisModule ptr.*/
|
static dict *modules; /* Hash table of modules. SDS -> RedisModule ptr.*/
|
||||||
|
|
||||||
/* Entries in the context->amqueue array, representing objects to free
|
/* Entries in the context->amqueue array, representing objects to free
|
||||||
@ -510,6 +522,22 @@ void RedisModuleCommandDispatcher(client *c) {
|
|||||||
cp->func(&ctx,(void**)c->argv,c->argc);
|
cp->func(&ctx,(void**)c->argv,c->argc);
|
||||||
moduleHandlePropagationAfterCommandCallback(&ctx);
|
moduleHandlePropagationAfterCommandCallback(&ctx);
|
||||||
moduleFreeContext(&ctx);
|
moduleFreeContext(&ctx);
|
||||||
|
|
||||||
|
/* In some cases processMultibulkBuffer uses sdsMakeRoomFor to
|
||||||
|
* expand the query buffer, and in order to avoid a big object copy
|
||||||
|
* the query buffer SDS may be used directly as the SDS string backing
|
||||||
|
* the client argument vectors: sometimes this will result in the SDS
|
||||||
|
* string having unused space at the end. Later if a module takes ownership
|
||||||
|
* of the RedisString, such space will be wasted forever. Inside the
|
||||||
|
* Redis core this is not a problem because tryObjectEncoding() is called
|
||||||
|
* before storing strings in the key space. Here we need to do it
|
||||||
|
* for the module. */
|
||||||
|
for (int i = 0; i < c->argc; i++) {
|
||||||
|
/* Only do the work if the module took ownership of the object:
|
||||||
|
* in that case the refcount is no longer 1. */
|
||||||
|
if (c->argv[i]->refcount > 1)
|
||||||
|
trimStringObjectIfNeeded(c->argv[i]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This function returns the list of keys, with the same interface as the
|
/* This function returns the list of keys, with the same interface as the
|
||||||
@ -702,6 +730,8 @@ void RM_SetModuleAttribs(RedisModuleCtx *ctx, const char *name, int ver, int api
|
|||||||
module->ver = ver;
|
module->ver = ver;
|
||||||
module->apiver = apiver;
|
module->apiver = apiver;
|
||||||
module->types = listCreate();
|
module->types = listCreate();
|
||||||
|
module->usedby = listCreate();
|
||||||
|
module->using = listCreate();
|
||||||
ctx->module = module;
|
ctx->module = module;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3430,6 +3460,8 @@ void RM_LogRaw(RedisModule *module, const char *levelstr, const char *fmt, va_li
|
|||||||
else if (!strcasecmp(levelstr,"warning")) level = LL_WARNING;
|
else if (!strcasecmp(levelstr,"warning")) level = LL_WARNING;
|
||||||
else level = LL_VERBOSE; /* Default. */
|
else level = LL_VERBOSE; /* Default. */
|
||||||
|
|
||||||
|
if (level < server.verbosity) return;
|
||||||
|
|
||||||
name_len = snprintf(msg, sizeof(msg),"<%s> ", module->name);
|
name_len = snprintf(msg, sizeof(msg),"<%s> ", module->name);
|
||||||
vsnprintf(msg + name_len, sizeof(msg) - name_len, fmt, ap);
|
vsnprintf(msg + name_len, sizeof(msg) - name_len, fmt, ap);
|
||||||
serverLogRaw(level,msg);
|
serverLogRaw(level,msg);
|
||||||
@ -4653,6 +4685,121 @@ void RM_GetRandomHexChars(char *dst, size_t len) {
|
|||||||
getRandomHexChars(dst,len);
|
getRandomHexChars(dst,len);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------
|
||||||
|
* Modules API exporting / importing
|
||||||
|
* -------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
/* This function is called by a module in order to export some API with a
|
||||||
|
* given name. Other modules will be able to use this API by calling the
|
||||||
|
* symmetrical function RM_GetSharedAPI() and casting the return value to
|
||||||
|
* the right function pointer.
|
||||||
|
*
|
||||||
|
* The function will return REDISMODULE_OK if the name is not already taken,
|
||||||
|
* otherwise REDISMODULE_ERR will be returned and no operation will be
|
||||||
|
* performed.
|
||||||
|
*
|
||||||
|
* IMPORTANT: the apiname argument should be a string literal with static
|
||||||
|
* lifetime. The API relies on the fact that it will always be valid in
|
||||||
|
* the future. */
|
||||||
|
int RM_ExportSharedAPI(RedisModuleCtx *ctx, const char *apiname, void *func) {
|
||||||
|
RedisModuleSharedAPI *sapi = zmalloc(sizeof(*sapi), MALLOC_LOCAL);
|
||||||
|
sapi->module = ctx->module;
|
||||||
|
sapi->func = func;
|
||||||
|
if (dictAdd(server.sharedapi, (char*)apiname, sapi) != DICT_OK) {
|
||||||
|
zfree(sapi);
|
||||||
|
return REDISMODULE_ERR;
|
||||||
|
}
|
||||||
|
return REDISMODULE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Request an exported API pointer. The return value is just a void pointer
|
||||||
|
* that the caller of this function will be required to cast to the right
|
||||||
|
* function pointer, so this is a private contract between modules.
|
||||||
|
*
|
||||||
|
* If the requested API is not available then NULL is returned. Because
|
||||||
|
* modules can be loaded at different times with different order, this
|
||||||
|
* function calls should be put inside some module generic API registering
|
||||||
|
* step, that is called every time a module attempts to execute a
|
||||||
|
* command that requires external APIs: if some API cannot be resolved, the
|
||||||
|
* command should return an error.
|
||||||
|
*
|
||||||
|
* Here is an exmaple:
|
||||||
|
*
|
||||||
|
* int ... myCommandImplementation() {
|
||||||
|
* if (getExternalAPIs() == 0) {
|
||||||
|
* reply with an error here if we cannot have the APIs
|
||||||
|
* }
|
||||||
|
* // Use the API:
|
||||||
|
* myFunctionPointer(foo);
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* And the function registerAPI() is:
|
||||||
|
*
|
||||||
|
* int getExternalAPIs(void) {
|
||||||
|
* static int api_loaded = 0;
|
||||||
|
* if (api_loaded != 0) return 1; // APIs already resolved.
|
||||||
|
*
|
||||||
|
* myFunctionPointer = RedisModule_GetOtherModuleAPI("...");
|
||||||
|
* if (myFunctionPointer == NULL) return 0;
|
||||||
|
*
|
||||||
|
* return 1;
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
void *RM_GetSharedAPI(RedisModuleCtx *ctx, const char *apiname) {
|
||||||
|
dictEntry *de = dictFind(server.sharedapi, apiname);
|
||||||
|
if (de == NULL) return NULL;
|
||||||
|
RedisModuleSharedAPI *sapi = dictGetVal(de);
|
||||||
|
if (listSearchKey(sapi->module->usedby,ctx->module) == NULL) {
|
||||||
|
listAddNodeTail(sapi->module->usedby,ctx->module);
|
||||||
|
listAddNodeTail(ctx->module->using,sapi->module);
|
||||||
|
}
|
||||||
|
return sapi->func;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Remove all the APIs registered by the specified module. Usually you
|
||||||
|
* want this when the module is going to be unloaded. This function
|
||||||
|
* assumes that's caller responsibility to make sure the APIs are not
|
||||||
|
* used by other modules.
|
||||||
|
*
|
||||||
|
* The number of unregistered APIs is returned. */
|
||||||
|
int moduleUnregisterSharedAPI(RedisModule *module) {
|
||||||
|
int count = 0;
|
||||||
|
dictIterator *di = dictGetSafeIterator(server.sharedapi);
|
||||||
|
dictEntry *de;
|
||||||
|
while ((de = dictNext(di)) != NULL) {
|
||||||
|
const char *apiname = dictGetKey(de);
|
||||||
|
RedisModuleSharedAPI *sapi = dictGetVal(de);
|
||||||
|
if (sapi->module == module) {
|
||||||
|
dictDelete(server.sharedapi,apiname);
|
||||||
|
zfree(sapi);
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dictReleaseIterator(di);
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Remove the specified module as an user of APIs of ever other module.
|
||||||
|
* This is usually called when a module is unloaded.
|
||||||
|
*
|
||||||
|
* Returns the number of modules this module was using APIs from. */
|
||||||
|
int moduleUnregisterUsedAPI(RedisModule *module) {
|
||||||
|
listIter li;
|
||||||
|
listNode *ln;
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
listRewind(module->using,&li);
|
||||||
|
while((ln = listNext(&li))) {
|
||||||
|
RedisModule *used = ln->value;
|
||||||
|
listNode *ln = listSearchKey(used->usedby,module);
|
||||||
|
if (ln) {
|
||||||
|
listDelNode(module->using,ln);
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
/* --------------------------------------------------------------------------
|
/* --------------------------------------------------------------------------
|
||||||
* Modules API internals
|
* Modules API internals
|
||||||
* -------------------------------------------------------------------------- */
|
* -------------------------------------------------------------------------- */
|
||||||
@ -4798,6 +4945,8 @@ int moduleLoad(const char *path, void **module_argv, int module_argc) {
|
|||||||
if (onload((void*)&ctx,module_argv,module_argc) == REDISMODULE_ERR) {
|
if (onload((void*)&ctx,module_argv,module_argc) == REDISMODULE_ERR) {
|
||||||
if (ctx.module) {
|
if (ctx.module) {
|
||||||
moduleUnregisterCommands(ctx.module);
|
moduleUnregisterCommands(ctx.module);
|
||||||
|
moduleUnregisterSharedAPI(ctx.module);
|
||||||
|
moduleUnregisterUsedAPI(ctx.module);
|
||||||
moduleFreeModuleStructure(ctx.module);
|
moduleFreeModuleStructure(ctx.module);
|
||||||
}
|
}
|
||||||
dlclose(handle);
|
dlclose(handle);
|
||||||
@ -4827,14 +4976,17 @@ int moduleUnload(sds name) {
|
|||||||
if (module == NULL) {
|
if (module == NULL) {
|
||||||
errno = ENOENT;
|
errno = ENOENT;
|
||||||
return REDISMODULE_ERR;
|
return REDISMODULE_ERR;
|
||||||
}
|
} else if (listLength(module->types)) {
|
||||||
|
|
||||||
if (listLength(module->types)) {
|
|
||||||
errno = EBUSY;
|
errno = EBUSY;
|
||||||
return REDISMODULE_ERR;
|
return REDISMODULE_ERR;
|
||||||
|
} else if (listLength(module->usedby)) {
|
||||||
|
errno = EPERM;
|
||||||
|
return REDISMODULE_ERR;
|
||||||
}
|
}
|
||||||
|
|
||||||
moduleUnregisterCommands(module);
|
moduleUnregisterCommands(module);
|
||||||
|
moduleUnregisterSharedAPI(module);
|
||||||
|
moduleUnregisterUsedAPI(module);
|
||||||
|
|
||||||
/* Remove any notification subscribers this module might have */
|
/* Remove any notification subscribers this module might have */
|
||||||
moduleUnsubscribeNotifications(module);
|
moduleUnsubscribeNotifications(module);
|
||||||
@ -4915,7 +5067,12 @@ NULL
|
|||||||
errmsg = "no such module with that name";
|
errmsg = "no such module with that name";
|
||||||
break;
|
break;
|
||||||
case EBUSY:
|
case EBUSY:
|
||||||
errmsg = "the module exports one or more module-side data types, can't unload";
|
errmsg = "the module exports one or more module-side data "
|
||||||
|
"types, can't unload";
|
||||||
|
break;
|
||||||
|
case EPERM:
|
||||||
|
errmsg = "the module exports APIs used by other modules. "
|
||||||
|
"Please unload them first and try again";
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
errmsg = "operation not possible.";
|
errmsg = "operation not possible.";
|
||||||
@ -4940,6 +5097,7 @@ size_t moduleCount(void) {
|
|||||||
* file so that's easy to seek it to add new entries. */
|
* file so that's easy to seek it to add new entries. */
|
||||||
void moduleRegisterCoreAPI(void) {
|
void moduleRegisterCoreAPI(void) {
|
||||||
server.moduleapi = dictCreate(&moduleAPIDictType,NULL);
|
server.moduleapi = dictCreate(&moduleAPIDictType,NULL);
|
||||||
|
server.sharedapi = dictCreate(&moduleAPIDictType,NULL);
|
||||||
REGISTER_API(Alloc);
|
REGISTER_API(Alloc);
|
||||||
REGISTER_API(Calloc);
|
REGISTER_API(Calloc);
|
||||||
REGISTER_API(Realloc);
|
REGISTER_API(Realloc);
|
||||||
@ -5090,4 +5248,6 @@ void moduleRegisterCoreAPI(void) {
|
|||||||
REGISTER_API(DictPrev);
|
REGISTER_API(DictPrev);
|
||||||
REGISTER_API(DictCompareC);
|
REGISTER_API(DictCompareC);
|
||||||
REGISTER_API(DictCompare);
|
REGISTER_API(DictCompare);
|
||||||
|
REGISTER_API(ExportSharedAPI);
|
||||||
|
REGISTER_API(GetSharedAPI);
|
||||||
}
|
}
|
||||||
|
34
src/object.c
34
src/object.c
@ -417,6 +417,18 @@ int isObjectRepresentableAsLongLong(robj *o, long long *llval) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Optimize the SDS string inside the string object to require little space,
|
||||||
|
* in case there is more than 10% of free space at the end of the SDS
|
||||||
|
* string. This happens because SDS strings tend to overallocate to avoid
|
||||||
|
* wasting too much time in allocations when appending to the string. */
|
||||||
|
void trimStringObjectIfNeeded(robj *o) {
|
||||||
|
if (o->encoding == OBJ_ENCODING_RAW &&
|
||||||
|
sdsavail(ptrFromObj(o)) > sdslen(ptrFromObj(o))/10)
|
||||||
|
{
|
||||||
|
o->m_ptr = sdsRemoveFreeSpace(ptrFromObj(o));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Try to encode a string object in order to save space */
|
/* Try to encode a string object in order to save space */
|
||||||
robj *tryObjectEncoding(robj *o) {
|
robj *tryObjectEncoding(robj *o) {
|
||||||
long value;
|
long value;
|
||||||
@ -486,11 +498,7 @@ robj *tryObjectEncoding(robj *o) {
|
|||||||
* We do that only for relatively large strings as this branch
|
* We do that only for relatively large strings as this branch
|
||||||
* is only entered if the length of the string is greater than
|
* is only entered if the length of the string is greater than
|
||||||
* OBJ_ENCODING_EMBSTR_SIZE_LIMIT. */
|
* OBJ_ENCODING_EMBSTR_SIZE_LIMIT. */
|
||||||
if (o->encoding == OBJ_ENCODING_RAW &&
|
trimStringObjectIfNeeded(o);
|
||||||
sdsavail(s) > len/10)
|
|
||||||
{
|
|
||||||
o->m_ptr = sdsRemoveFreeSpace(ptrFromObj(o));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Return the original object. */
|
/* Return the original object. */
|
||||||
return o;
|
return o;
|
||||||
@ -1208,16 +1216,20 @@ void objectSetLRUOrLFU(robj *val, long long lfu_freq, long long lru_idle,
|
|||||||
val->lru = (LFUGetTimeInMinutes()<<8) | lfu_freq;
|
val->lru = (LFUGetTimeInMinutes()<<8) | lfu_freq;
|
||||||
}
|
}
|
||||||
} else if (lru_idle >= 0) {
|
} else if (lru_idle >= 0) {
|
||||||
/* Serialized LRU idle time is in seconds. Scale
|
/* Provided LRU idle time is in seconds. Scale
|
||||||
* according to the LRU clock resolution this Redis
|
* according to the LRU clock resolution this Redis
|
||||||
* instance was compiled with (normally 1000 ms, so the
|
* instance was compiled with (normally 1000 ms, so the
|
||||||
* below statement will expand to lru_idle*1000/1000. */
|
* below statement will expand to lru_idle*1000/1000. */
|
||||||
lru_idle = lru_idle*1000/LRU_CLOCK_RESOLUTION;
|
lru_idle = lru_idle*1000/LRU_CLOCK_RESOLUTION;
|
||||||
val->lru = lru_clock - lru_idle;
|
long lru_abs = lru_clock - lru_idle; /* Absolute access time. */
|
||||||
/* If the lru field overflows (since LRU it is a wrapping
|
/* If the LRU field underflows (since LRU it is a wrapping
|
||||||
* clock), the best we can do is to provide the maximum
|
* clock), the best we can do is to provide a large enough LRU
|
||||||
* representable idle time. */
|
* that is half-way in the circlular LRU clock we use: this way
|
||||||
if (val->lru < 0) val->lru = lru_clock+1;
|
* the computed idle time for this object will stay high for quite
|
||||||
|
* some time. */
|
||||||
|
if (lru_abs < 0)
|
||||||
|
lru_abs = (lru_clock+(LRU_CLOCK_MAX/2)) % LRU_CLOCK_MAX;
|
||||||
|
val->lru = lru_abs;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -335,6 +335,8 @@ void REDISMODULE_API_FUNC(RedisModule_GetRandomBytes)(unsigned char *dst, size_t
|
|||||||
void REDISMODULE_API_FUNC(RedisModule_GetRandomHexChars)(char *dst, size_t len);
|
void REDISMODULE_API_FUNC(RedisModule_GetRandomHexChars)(char *dst, size_t len);
|
||||||
void REDISMODULE_API_FUNC(RedisModule_SetDisconnectCallback)(RedisModuleBlockedClient *bc, RedisModuleDisconnectFunc callback);
|
void REDISMODULE_API_FUNC(RedisModule_SetDisconnectCallback)(RedisModuleBlockedClient *bc, RedisModuleDisconnectFunc callback);
|
||||||
void REDISMODULE_API_FUNC(RedisModule_SetClusterFlags)(RedisModuleCtx *ctx, uint64_t flags);
|
void REDISMODULE_API_FUNC(RedisModule_SetClusterFlags)(RedisModuleCtx *ctx, uint64_t flags);
|
||||||
|
int REDISMODULE_API_FUNC(RedisModule_ExportSharedAPI)(RedisModuleCtx *ctx, const char *apiname, void *func);
|
||||||
|
void *REDISMODULE_API_FUNC(RedisModule_GetSharedAPI)(RedisModuleCtx *ctx, const char *apiname);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* This is included inline inside each Redis module. */
|
/* This is included inline inside each Redis module. */
|
||||||
@ -495,6 +497,8 @@ static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int
|
|||||||
REDISMODULE_GET_API(GetRandomBytes);
|
REDISMODULE_GET_API(GetRandomBytes);
|
||||||
REDISMODULE_GET_API(GetRandomHexChars);
|
REDISMODULE_GET_API(GetRandomHexChars);
|
||||||
REDISMODULE_GET_API(SetClusterFlags);
|
REDISMODULE_GET_API(SetClusterFlags);
|
||||||
|
REDISMODULE_GET_API(ExportSharedAPI);
|
||||||
|
REDISMODULE_GET_API(GetSharedAPI);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (RedisModule_IsModuleNameBusy && RedisModule_IsModuleNameBusy(name)) return REDISMODULE_ERR;
|
if (RedisModule_IsModuleNameBusy && RedisModule_IsModuleNameBusy(name)) return REDISMODULE_ERR;
|
||||||
|
@ -259,8 +259,12 @@ sds sdsRemoveFreeSpace(sds s) {
|
|||||||
char type, oldtype = s[-1] & SDS_TYPE_MASK;
|
char type, oldtype = s[-1] & SDS_TYPE_MASK;
|
||||||
int hdrlen, oldhdrlen = sdsHdrSize(oldtype);
|
int hdrlen, oldhdrlen = sdsHdrSize(oldtype);
|
||||||
size_t len = sdslen(s);
|
size_t len = sdslen(s);
|
||||||
|
size_t avail = sdsavail(s);
|
||||||
sh = (char*)s-oldhdrlen;
|
sh = (char*)s-oldhdrlen;
|
||||||
|
|
||||||
|
/* Return ASAP if there is no space left. */
|
||||||
|
if (avail == 0) return s;
|
||||||
|
|
||||||
/* Check what would be the minimum SDS header that is just good enough to
|
/* Check what would be the minimum SDS header that is just good enough to
|
||||||
* fit this string. */
|
* fit this string. */
|
||||||
type = sdsReqType(len);
|
type = sdsReqType(len);
|
||||||
|
@ -1087,7 +1087,9 @@ struct redisServer {
|
|||||||
size_t initial_memory_usage; /* Bytes used after initialization. */
|
size_t initial_memory_usage; /* Bytes used after initialization. */
|
||||||
int always_show_logo; /* Show logo even for non-stdout logging. */
|
int always_show_logo; /* Show logo even for non-stdout logging. */
|
||||||
/* Modules */
|
/* Modules */
|
||||||
dict *moduleapi; /* Exported APIs dictionary for modules. */
|
dict *moduleapi; /* Exported core APIs dictionary for modules. */
|
||||||
|
dict *sharedapi; /* Like moduleapi but containing the APIs that
|
||||||
|
modules share with each other. */
|
||||||
list *loadmodule_queue; /* List of modules to load at startup. */
|
list *loadmodule_queue; /* List of modules to load at startup. */
|
||||||
int module_blocked_pipe[2]; /* Pipe used to awake the event loop if a
|
int module_blocked_pipe[2]; /* Pipe used to awake the event loop if a
|
||||||
client blocked on a module command needs
|
client blocked on a module command needs
|
||||||
@ -1740,6 +1742,7 @@ int compareStringObjects(robj *a, robj *b);
|
|||||||
int collateStringObjects(robj *a, robj *b);
|
int collateStringObjects(robj *a, robj *b);
|
||||||
int equalStringObjects(robj *a, robj *b);
|
int equalStringObjects(robj *a, robj *b);
|
||||||
unsigned long long estimateObjectIdleTime(robj *o);
|
unsigned long long estimateObjectIdleTime(robj *o);
|
||||||
|
void trimStringObjectIfNeeded(robj *o);
|
||||||
#define sdsEncodedObject(objptr) (objptr->encoding == OBJ_ENCODING_RAW || objptr->encoding == OBJ_ENCODING_EMBSTR)
|
#define sdsEncodedObject(objptr) (objptr->encoding == OBJ_ENCODING_RAW || objptr->encoding == OBJ_ENCODING_EMBSTR)
|
||||||
|
|
||||||
/* Synchronous I/O with timeout */
|
/* Synchronous I/O with timeout */
|
||||||
|
@ -615,6 +615,10 @@ void hincrbyfloatCommand(client *c) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
value += incr;
|
value += incr;
|
||||||
|
if (isnan(value) || isinf(value)) {
|
||||||
|
addReplyError(c,"increment would produce NaN or Infinity");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
char buf[MAX_LONG_DOUBLE_CHARS];
|
char buf[MAX_LONG_DOUBLE_CHARS];
|
||||||
int len = ld2string(buf,sizeof(buf),value,1);
|
int len = ld2string(buf,sizeof(buf),value,1);
|
||||||
|
@ -520,7 +520,7 @@ void lremCommand(client *c) {
|
|||||||
|
|
||||||
if (removed) {
|
if (removed) {
|
||||||
signalModifiedKey(c->db,c->argv[1]);
|
signalModifiedKey(c->db,c->argv[1]);
|
||||||
notifyKeyspaceEvent(NOTIFY_GENERIC,"lrem",c->argv[1],c->db->id);
|
notifyKeyspaceEvent(NOTIFY_LIST,"lrem",c->argv[1],c->db->id);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (listTypeLength(subject) == 0) {
|
if (listTypeLength(subject) == 0) {
|
||||||
|
10
src/t_zset.c
10
src/t_zset.c
@ -2906,7 +2906,10 @@ void genericZrangebylexCommand(client *c, int reverse) {
|
|||||||
while (remaining) {
|
while (remaining) {
|
||||||
if (remaining >= 3 && !strcasecmp(ptrFromObj(c->argv[pos]),"limit")) {
|
if (remaining >= 3 && !strcasecmp(ptrFromObj(c->argv[pos]),"limit")) {
|
||||||
if ((getLongFromObjectOrReply(c, c->argv[pos+1], &offset, NULL) != C_OK) ||
|
if ((getLongFromObjectOrReply(c, c->argv[pos+1], &offset, NULL) != C_OK) ||
|
||||||
(getLongFromObjectOrReply(c, c->argv[pos+2], &limit, NULL) != C_OK)) return;
|
(getLongFromObjectOrReply(c, c->argv[pos+2], &limit, NULL) != C_OK)) {
|
||||||
|
zslFreeLexRange(&range);
|
||||||
|
return;
|
||||||
|
}
|
||||||
pos += 3; remaining -= 3;
|
pos += 3; remaining -= 3;
|
||||||
} else {
|
} else {
|
||||||
zslFreeLexRange(&range);
|
zslFreeLexRange(&range);
|
||||||
@ -3140,7 +3143,10 @@ void genericZpopCommand(client *c, robj **keyv, int keyc, int where, int emitkey
|
|||||||
if (countarg) {
|
if (countarg) {
|
||||||
if (getLongFromObjectOrReply(c,countarg,&count,NULL) != C_OK)
|
if (getLongFromObjectOrReply(c,countarg,&count,NULL) != C_OK)
|
||||||
return;
|
return;
|
||||||
if (count < 0) count = 1;
|
if (count <= 0) {
|
||||||
|
addReply(c,shared.emptyarray);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check type and break on the first error, otherwise identify candidate. */
|
/* Check type and break on the first error, otherwise identify candidate. */
|
||||||
|
@ -447,7 +447,7 @@ int string2l(const char *s, size_t slen, long *lval) {
|
|||||||
* a double: no spaces or other characters before or after the string
|
* a double: no spaces or other characters before or after the string
|
||||||
* representing the number are accepted. */
|
* representing the number are accepted. */
|
||||||
int string2ld(const char *s, size_t slen, long double *dp) {
|
int string2ld(const char *s, size_t slen, long double *dp) {
|
||||||
char buf[256];
|
char buf[MAX_LONG_DOUBLE_CHARS];
|
||||||
long double value;
|
long double value;
|
||||||
char *eptr;
|
char *eptr;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user