Unverified Commit 2667c412 authored by Oran Agra's avatar Oran Agra Committed by GitHub
Browse files

Merge pull request #10829 from oranagra/release-7.0.1

Release 7.0.1
parents d375595d 595e725d
......@@ -11,7 +11,8 @@
"NOSCRIPT",
"SKIP_MONITOR",
"NO_MANDATORY_KEYS",
"STALE"
"STALE",
"READONLY"
],
"acl_categories": [
"SCRIPTING"
......
......@@ -41,7 +41,7 @@
{
"name": "key2",
"type": "key",
"key_spec_index": 1
"key_spec_index": 0
},
{
"name": "len",
......
......@@ -4,7 +4,7 @@
"complexity": "N/A",
"group": "hyperloglog",
"since": "2.8.9",
"arity": -3,
"arity": 3,
"function": "pfdebugCommand",
"doc_flags": [
"SYSCMD"
......@@ -36,6 +36,17 @@
}
}
}
],
"arguments": [
{
"name": "subcommand",
"type": "string"
},
{
"name": "key",
"type": "key",
"key_spec_index": 0
}
]
}
}
{
"SHARDCHANNELS": {
"summary": "List active shard channels",
"complexity": "O(N) where N is the number of active shard channels, and assuming constant time pattern matching (relatively short channels).",
"complexity": "O(N) where N is the number of active shard channels, and assuming constant time pattern matching (relatively short shard channels).",
"group": "pubsub",
"since": "7.0.0",
"arity": -2,
......
{
"SHARDNUMSUB": {
"summary": "Get the count of subscribers for shard channels",
"complexity": "O(N) for the SHARDNUMSUB subcommand, where N is the number of requested channels",
"complexity": "O(N) for the SHARDNUMSUB subcommand, where N is the number of requested shard channels",
"group": "pubsub",
"since": "7.0.0",
"arity": -2,
......@@ -14,7 +14,7 @@
],
"arguments": [
{
"name": "channel",
"name": "shardchannel",
"type": "string",
"optional": true,
"multiple": true
......
......@@ -62,7 +62,7 @@
{
"name": "newkey",
"type": "key",
"key_spec_index": 0
"key_spec_index": 1
}
]
}
......
......@@ -67,7 +67,7 @@
{
"name": "newkey",
"type": "key",
"key_spec_index": 0
"key_spec_index": 1
}
]
}
......
......@@ -6,6 +6,20 @@
"since": "3.0.0",
"arity": -4,
"function": "restoreCommand",
"history": [
[
"3.0.0",
"Added the `REPLACE` modifier."
],
[
"5.0.0",
"Added the `ABSTTL` modifier."
],
[
"5.0.0",
"Added the `IDLETIME` and `FREQ` options."
]
],
"doc_flags": [
"SYSCMD"
],
......@@ -37,6 +51,49 @@
}
}
}
],
"arguments": [
{
"name": "key",
"type": "key",
"key_spec_index": 0
},
{
"name": "ttl",
"type": "integer"
},
{
"name": "serialized-value",
"type": "string"
},
{
"name": "replace",
"token": "REPLACE",
"type": "pure-token",
"optional": true,
"since": "3.0.0"
},
{
"name": "absttl",
"token": "ABSTTL",
"type": "pure-token",
"optional": true,
"since": "5.0.0"
},
{
"token": "IDLETIME",
"name": "seconds",
"type": "integer",
"optional": true,
"since": "5.0.0"
},
{
"token": "FREQ",
"name": "frequency",
"type": "integer",
"optional": true,
"since": "5.0.0"
}
]
}
}
{
"SLAVES": {
"summary": "List the monitored slaves",
"complexity": "O(N) where N is the number of slaves",
"group": "sentinel",
"since": "2.8.0",
"arity": 3,
"container": "SENTINEL",
"function": "sentinelCommand",
"deprecated_since": "5.0.0",
"replaced_by": "`SENTINEL REPLICAS`",
"doc_flags": [
"DEPRECATED"
],
"command_flags": [
"ADMIN",
"SENTINEL",
"ONLY_SENTINEL"
],
"arguments": [
{
"name": "master-name",
"type": "string"
}
]
}
}
......@@ -15,7 +15,7 @@
],
"arguments": [
{
"name": "channel",
"name": "shardchannel",
"type": "string"
},
{
......
......@@ -14,7 +14,7 @@
],
"arguments": [
{
"name": "channel",
"name": "shardchannel",
"type": "string",
"multiple": true
}
......
{
"SUNSUBSCRIBE": {
"summary": "Stop listening for messages posted to the given shard channels",
"complexity": "O(N) where N is the number of clients already subscribed to a channel.",
"complexity": "O(N) where N is the number of clients already subscribed to a shard channel.",
"group": "pubsub",
"since": "7.0.0",
"arity": -1,
......@@ -14,7 +14,7 @@
],
"arguments": [
{
"name": "channel",
"name": "shardchannel",
"type": "string",
"optional": true,
"multiple": true
......
......@@ -18,6 +18,9 @@
],
"key_specs": [
{
"flags": [
"RO"
],
"begin_search": {
"index": {
"pos": 1
......
......@@ -7,6 +7,10 @@
"arity": -3,
"container": "XINFO",
"history": [
[
"6.0.0",
"Added the `FULL` modifier."
],
[
"7.0.0",
"Added the `max-deleted-entry-id`, `entries-added`, `recorded-first-entry-id`, `entries-read` and `lag` fields"
......
......@@ -311,18 +311,21 @@ int configEnumGetValue(configEnum *ce, sds *argv, int argc, int bitflags) {
/* Get enum name/s from value. If no matches are found "unknown" is returned. */
static sds configEnumGetName(configEnum *ce, int values, int bitflags) {
sds names = NULL;
int matches = 0;
int unmatched = values;
for( ; ce->name != NULL; ce++) {
if (values == ce->val) { /* Short path for perfect match */
sdsfree(names);
return sdsnew(ce->name);
}
if (bitflags && (values & ce->val)) {
/* Note: for bitflags, we want them sorted from high to low, so that if there are several / partially
* overlapping entries, we'll prefer the ones matching more bits. */
if (bitflags && ce->val && ce->val == (unmatched & ce->val)) {
names = names ? sdscatfmt(names, " %s", ce->name) : sdsnew(ce->name);
matches |= ce->val;
unmatched &= ~ce->val;
}
}
if (!names || values != matches) {
if (!names || unmatched) {
sdsfree(names);
return sdsnew("unknown");
}
......@@ -484,9 +487,23 @@ void loadServerConfigFromString(char *config) {
err = "wrong number of arguments";
goto loaderr;
}
/* Set config using all arguments that follows */
if (!config->interface.set(config, &argv[1], argc-1, &err)) {
goto loaderr;
if ((config->flags & MULTI_ARG_CONFIG) && argc == 2 && sdslen(argv[1])) {
/* For MULTI_ARG_CONFIGs, if we only have one argument, try to split it by spaces.
* Only if the argument is not empty, otherwise something like --save "" will fail.
* So that we can support something like --config "arg1 arg2 arg3". */
sds *new_argv;
int new_argc;
new_argv = sdssplitargs(argv[1], &new_argc);
if (!config->interface.set(config, new_argv, new_argc, &err)) {
goto loaderr;
}
sdsfreesplitres(new_argv, new_argc);
} else {
/* Set config using all arguments that follows */
if (!config->interface.set(config, &argv[1], argc-1, &err)) {
goto loaderr;
}
}
sdsfreesplitres(argv,argc);
......@@ -1125,10 +1142,21 @@ struct rewriteConfigState *rewriteConfigReadOldFile(char *path) {
/* Not a comment, split into arguments. */
argv = sdssplitargs(line,&argc);
if (argv == NULL || (!server.sentinel_mode && !lookupConfig(argv[0]))) {
/* Apparently the line is unparsable for some reason, for
* instance it may have unbalanced quotes, or may contain a
* config that doesn't exist anymore. Load it as a comment. */
if (argv == NULL ||
(!lookupConfig(argv[0]) &&
/* The following is a list of config features that are only supported in
* config file parsing and are not recognized by lookupConfig */
strcasecmp(argv[0],"include") &&
strcasecmp(argv[0],"rename-command") &&
strcasecmp(argv[0],"user") &&
strcasecmp(argv[0],"loadmodule") &&
strcasecmp(argv[0],"sentinel")))
{
/* The line is either unparsable for some reason, for
* instance it may have unbalanced quotes, may contain a
* config that doesn't exist anymore, for instance a module that got
* unloaded. Load it as a comment. */
sds aux = sdsnew("# ??? ");
aux = sdscatsds(aux,line);
if (argv) sdsfreesplitres(argv, argc);
......@@ -1143,18 +1171,13 @@ struct rewriteConfigState *rewriteConfigReadOldFile(char *path) {
* Append the line and populate the option -> line numbers map. */
rewriteConfigAppendLine(state,line);
/* Translate options using the word "slave" to the corresponding name
* "replica", before adding such option to the config name -> lines
* mapping. */
char *p = strstr(argv[0],"slave");
if (p) {
sds alt = sdsempty();
alt = sdscatlen(alt,argv[0],p-argv[0]);
alt = sdscatlen(alt,"replica",7);
alt = sdscatlen(alt,p+5,strlen(p+5));
/* If this is a alias config, replace it with the original name. */
standardConfig *s_conf = lookupConfig(argv[0]);
if (s_conf && s_conf->flags & ALIAS_CONFIG) {
sdsfree(argv[0]);
argv[0] = alt;
argv[0] = sdsnew(s_conf->alias);
}
/* If this is sentinel config, we use sentinel "sentinel <config>" as option
to avoid messing up the sequence. */
if (server.sentinel_mode && argc > 1 && !strcasecmp(argv[0],"sentinel")) {
......@@ -1796,7 +1819,7 @@ static int boolConfigSet(standardConfig *config, sds *argv, int argc, const char
*(config->data.yesno.config) = yn;
return 1;
}
return 2;
return (config->flags & VOLATILE_CONFIG) ? 1 : 2;
}
static sds boolConfigGet(standardConfig *config) {
......@@ -1838,7 +1861,7 @@ static int stringConfigSet(standardConfig *config, sds *argv, int argc, const ch
zfree(prev);
return 1;
}
return 2;
return (config->flags & VOLATILE_CONFIG) ? 1 : 2;
}
static sds stringConfigGet(standardConfig *config) {
......@@ -1875,7 +1898,7 @@ static int sdsConfigSet(standardConfig *config, sds *argv, int argc, const char
return 1;
}
if (config->flags & MODULE_CONFIG && prev) sdsfree(prev);
return 2;
return (config->flags & VOLATILE_CONFIG) ? 1 : 2;
}
static sds sdsConfigGet(standardConfig *config) {
......@@ -1959,7 +1982,7 @@ static int enumConfigSet(standardConfig *config, sds *argv, int argc, const char
*(config->data.enumd.config) = enumval;
return 1;
}
return 2;
return (config->flags & VOLATILE_CONFIG) ? 1 : 2;
}
static sds enumConfigGet(standardConfig *config) {
......@@ -2155,7 +2178,7 @@ static int numericConfigSet(standardConfig *config, sds *argv, int argc, const c
return setNumericType(config, ll, err);
}
return 2;
return (config->flags & VOLATILE_CONFIG) ? 1 : 2;
}
static sds numericConfigGet(standardConfig *config) {
......@@ -2592,8 +2615,10 @@ static int setConfigSaveOption(standardConfig *config, sds *argv, int argc, cons
int j;
/* Special case: treat single arg "" as zero args indicating empty save configuration */
if (argc == 1 && !strcasecmp(argv[0],""))
if (argc == 1 && !strcasecmp(argv[0],"")) {
resetServerSaveParams();
argc = 0;
}
/* Perform sanity check before setting the new config:
* - Even number of args
......@@ -3078,15 +3103,15 @@ standardConfig static_configs[] = {
createEnumConfig("tls-auth-clients", NULL, MODIFIABLE_CONFIG, tls_auth_clients_enum, server.tls_auth_clients, TLS_CLIENT_AUTH_YES, NULL, NULL),
createBoolConfig("tls-prefer-server-ciphers", NULL, MODIFIABLE_CONFIG, server.tls_ctx_config.prefer_server_ciphers, 0, NULL, applyTlsCfg),
createBoolConfig("tls-session-caching", NULL, MODIFIABLE_CONFIG, server.tls_ctx_config.session_caching, 1, NULL, applyTlsCfg),
createStringConfig("tls-cert-file", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.cert_file, NULL, NULL, applyTlsCfg),
createStringConfig("tls-key-file", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.key_file, NULL, NULL, applyTlsCfg),
createStringConfig("tls-cert-file", NULL, VOLATILE_CONFIG | MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.cert_file, NULL, NULL, applyTlsCfg),
createStringConfig("tls-key-file", NULL, VOLATILE_CONFIG | MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.key_file, NULL, NULL, applyTlsCfg),
createStringConfig("tls-key-file-pass", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.key_file_pass, NULL, NULL, applyTlsCfg),
createStringConfig("tls-client-cert-file", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.client_cert_file, NULL, NULL, applyTlsCfg),
createStringConfig("tls-client-key-file", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.client_key_file, NULL, NULL, applyTlsCfg),
createStringConfig("tls-client-cert-file", NULL, VOLATILE_CONFIG | MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.client_cert_file, NULL, NULL, applyTlsCfg),
createStringConfig("tls-client-key-file", NULL, VOLATILE_CONFIG | MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.client_key_file, NULL, NULL, applyTlsCfg),
createStringConfig("tls-client-key-file-pass", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.client_key_file_pass, NULL, NULL, applyTlsCfg),
createStringConfig("tls-dh-params-file", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.dh_params_file, NULL, NULL, applyTlsCfg),
createStringConfig("tls-ca-cert-file", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.ca_cert_file, NULL, NULL, applyTlsCfg),
createStringConfig("tls-ca-cert-dir", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.ca_cert_dir, NULL, NULL, applyTlsCfg),
createStringConfig("tls-dh-params-file", NULL, VOLATILE_CONFIG | MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.dh_params_file, NULL, NULL, applyTlsCfg),
createStringConfig("tls-ca-cert-file", NULL, VOLATILE_CONFIG | MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.ca_cert_file, NULL, NULL, applyTlsCfg),
createStringConfig("tls-ca-cert-dir", NULL, VOLATILE_CONFIG | MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.ca_cert_dir, NULL, NULL, applyTlsCfg),
createStringConfig("tls-protocols", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.protocols, NULL, NULL, applyTlsCfg),
createStringConfig("tls-ciphers", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.ciphers, NULL, NULL, applyTlsCfg),
createStringConfig("tls-ciphersuites", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.tls_ctx_config.ciphersuites, NULL, NULL, applyTlsCfg),
......
......@@ -602,18 +602,11 @@ void flushAllDataAndResetRDB(int flags) {
server.dirty += emptyData(-1,flags,NULL);
if (server.child_type == CHILD_TYPE_RDB) killRDBChild();
if (server.saveparamslen > 0) {
/* Normally rdbSave() will reset dirty, but we don't want this here
* as otherwise FLUSHALL will not be replicated nor put into the AOF. */
int saved_dirty = server.dirty;
rdbSaveInfo rsi, *rsiptr;
rsiptr = rdbPopulateSaveInfo(&rsi);
rdbSave(SLAVE_REQ_NONE,server.rdb_filename,rsiptr);
server.dirty = saved_dirty;
}
/* Without that extra dirty++, when db was already empty, FLUSHALL will
* not be replicated nor put into the AOF. */
server.dirty++;
#if defined(USE_JEMALLOC)
/* jemalloc 5 doesn't release pages back to the OS when there's no traffic.
* for large databases, flushdb blocks for long anyway, so a bit more won't
......@@ -632,7 +625,13 @@ void flushdbCommand(client *c) {
if (getFlushCommandFlags(c,&flags) == C_ERR) return;
/* flushdb should not flush the functions */
server.dirty += emptyData(c->db->id,flags | EMPTYDB_NOFUNCTIONS,NULL);
/* Without the forceCommandPropagation, when DB was already empty,
* FLUSHDB will not be replicated nor put into the AOF. */
forceCommandPropagation(c, PROPAGATE_REPL | PROPAGATE_AOF);
addReply(c,shared.ok);
#if defined(USE_JEMALLOC)
/* jemalloc 5 doesn't release pages back to the OS when there's no traffic.
* for large databases, flushdb blocks for long anyway, so a bit more won't
......@@ -650,6 +649,11 @@ void flushallCommand(client *c) {
if (getFlushCommandFlags(c,&flags) == C_ERR) return;
/* flushall should not flush the functions */
flushAllDataAndResetRDB(flags | EMPTYDB_NOFUNCTIONS);
/* Without the forceCommandPropagation, when DBs were already empty,
* FLUSHALL will not be replicated nor put into the AOF. */
forceCommandPropagation(c, PROPAGATE_REPL | PROPAGATE_AOF);
addReply(c,shared.ok);
}
......@@ -1080,7 +1084,7 @@ void shutdownCommand(client *c) {
return;
}
if (!(flags & SHUTDOWN_NOSAVE) && scriptIsTimedout()) {
if (!(flags & SHUTDOWN_NOSAVE) && isInsideYieldingLongCommand()) {
/* Script timed out. Shutdown allowed only with the NOSAVE flag. See
* also processCommand where these errors are returned. */
if (server.busy_module_yield_flags && server.busy_module_yield_reply) {
......@@ -1878,7 +1882,6 @@ int getKeysFromCommandWithSpecs(struct redisCommand *cmd, robj **argv, int argc,
/* Flags indicating that we have a getkeys callback */
int has_module_getkeys = cmd->flags & CMD_MODULE_GETKEYS;
int has_native_getkeys = !(cmd->flags & CMD_MODULE) && cmd->getkeys_proc;
/* The key-spec that's auto generated by RM_CreateCommand sets VARIABLE_FLAGS since no flags are given.
* If the module provides getkeys callback, we'll prefer it, but if it didn't, we'll use key-spec anyway. */
......@@ -1900,14 +1903,14 @@ int getKeysFromCommandWithSpecs(struct redisCommand *cmd, robj **argv, int argc,
/* We use native getkeys as a last resort, since not all these native getkeys provide
* flags properly (only the ones that correspond to INVALID, INCOMPLETE or VARIABLE_FLAGS do.*/
if (has_native_getkeys)
if (cmd->getkeys_proc)
return cmd->getkeys_proc(cmd,argv,argc,result);
return 0;
}
/* This function returns a sanity check if the command may have keys. */
int doesCommandHaveKeys(struct redisCommand *cmd) {
return (!(cmd->flags & CMD_MODULE) && cmd->getkeys_proc) || /* has getkeys_proc (non modules) */
return cmd->getkeys_proc || /* has getkeys_proc (non modules) */
(cmd->flags & CMD_MODULE_GETKEYS) || /* module with GETKEYS */
(getAllKeySpecsFlags(cmd, 1) & CMD_KEY_NOT_KEY); /* has at least one key-spec not marked as NOT_KEY */
}
......@@ -2056,7 +2059,7 @@ int getKeysUsingLegacyRangeSpec(struct redisCommand *cmd, robj **argv, int argc,
int getKeysFromCommand(struct redisCommand *cmd, robj **argv, int argc, getKeysResult *result) {
if (cmd->flags & CMD_MODULE_GETKEYS) {
return moduleGetCommandKeysViaAPI(cmd,argv,argc,result);
} else if (!(cmd->flags & CMD_MODULE) && cmd->getkeys_proc) {
} else if (cmd->getkeys_proc) {
return cmd->getkeys_proc(cmd,argv,argc,result);
} else {
return getKeysUsingLegacyRangeSpec(cmd,argv,argc,result);
......
......@@ -2014,7 +2014,9 @@ void bugReportEnd(int killViaSignal, int sig) {
"\n=== REDIS BUG REPORT END. Make sure to include from START to END. ===\n\n"
" Please report the crash by opening an issue on github:\n\n"
" http://github.com/redis/redis/issues\n\n"
" If a Redis module was involved, please open in the module's repo instead.\n\n"
" Suspect RAM error? Use redis-server --test-memory to verify it.\n\n"
" Some other issues could be detected by redis-server --check-system\n"
);
/* free(messages); Don't call free() with possibly corrupted memory. */
......
......@@ -285,59 +285,61 @@ void scriptingReset(int async) {
* EVAL and SCRIPT commands implementation
* ------------------------------------------------------------------------- */
/* Define a Lua function with the specified body.
* The function name will be generated in the following form:
*
* f_<hex sha1 sum>
*
* The function increments the reference count of the 'body' object as a
* side effect of a successful call.
*
* On success a pointer to an SDS string representing the function SHA1 of the
* just added function is returned (and will be valid until the next call
* to scriptingReset() function), otherwise NULL is returned.
*
* The function handles the fact of being called with a script that already
* exists, and in such a case, it behaves like in the success case.
*
* If 'c' is not NULL, on error the client is informed with an appropriate
* error describing the nature of the problem and the Lua interpreter error. */
sds luaCreateFunction(client *c, robj *body) {
char funcname[43];
dictEntry *de;
uint64_t script_flags = SCRIPT_FLAG_EVAL_COMPAT_MODE;
funcname[0] = 'f';
funcname[1] = '_';
sha1hex(funcname+2,body->ptr,sdslen(body->ptr));
static void evalCalcFunctionName(int evalsha, sds script, char *out_funcname) {
/* We obtain the script SHA1, then check if this function is already
* defined into the Lua state */
out_funcname[0] = 'f';
out_funcname[1] = '_';
if (!evalsha) {
/* Hash the code if this is an EVAL call */
sha1hex(out_funcname+2,script,sdslen(script));
} else {
/* We already have the SHA if it is an EVALSHA */
int j;
char *sha = script;
if ((de = dictFind(lctx.lua_scripts,funcname+2)) != NULL) {
return dictGetKey(de);
/* Convert to lowercase. We don't use tolower since the function
* managed to always show up in the profiler output consuming
* a non trivial amount of time. */
for (j = 0; j < 40; j++)
out_funcname[j+2] = (sha[j] >= 'A' && sha[j] <= 'Z') ?
sha[j]+('a'-'A') : sha[j];
out_funcname[42] = '\0';
}
}
/* Handle shebang header in script code */
/* Helper function to try and extract shebang flags from the script body.
* If no shebang is found, return with success and COMPAT mode flag.
* The err arg is optional, can be used to get a detailed error string.
* The out_shebang_len arg is optional, can be used to trim the shebang from the script.
* Returns C_OK on success, and C_ERR on error. */
int evalExtractShebangFlags(sds body, uint64_t *out_flags, ssize_t *out_shebang_len, sds *err) {
ssize_t shebang_len = 0;
if (!strncmp(body->ptr, "#!", 2)) {
uint64_t script_flags = SCRIPT_FLAG_EVAL_COMPAT_MODE;
if (!strncmp(body, "#!", 2)) {
int numparts,j;
char *shebang_end = strchr(body->ptr, '\n');
char *shebang_end = strchr(body, '\n');
if (shebang_end == NULL) {
addReplyError(c,"Invalid script shebang");
return NULL;
if (err)
*err = sdsnew("Invalid script shebang");
return C_ERR;
}
shebang_len = shebang_end - (char*)body->ptr;
sds shebang = sdsnewlen(body->ptr, shebang_len);
shebang_len = shebang_end - body;
sds shebang = sdsnewlen(body, shebang_len);
sds *parts = sdssplitargs(shebang, &numparts);
sdsfree(shebang);
if (!parts || numparts == 0) {
addReplyError(c,"Invalid engine in script shebang");
if (err)
*err = sdsnew("Invalid engine in script shebang");
sdsfreesplitres(parts, numparts);
return NULL;
return C_ERR;
}
/* Verify lua interpreter was specified */
if (strcmp(parts[0], "#!lua")) {
addReplyErrorFormat(c,"Unexpected engine in script shebang: %s", parts[0]);
if (err)
*err = sdscatfmt(sdsempty(), "Unexpected engine in script shebang: %s", parts[0]);
sdsfreesplitres(parts, numparts);
return NULL;
return C_ERR;
}
script_flags &= ~SCRIPT_FLAG_EVAL_COMPAT_MODE;
for (j = 1; j < numparts; j++) {
......@@ -351,23 +353,93 @@ sds luaCreateFunction(client *c, robj *body) {
if (!strcmp(flags[jj], sf->str)) break;
}
if (!sf->flag) {
addReplyErrorFormat(c,"Unexpected flag in script shebang: %s", flags[jj]);
if (err)
*err = sdscatfmt(sdsempty(), "Unexpected flag in script shebang: %s", flags[jj]);
sdsfreesplitres(flags, numflags);
sdsfreesplitres(parts, numparts);
return NULL;
return C_ERR;
}
script_flags |= sf->flag;
}
sdsfreesplitres(flags, numflags);
} else {
/* We only support function flags options for lua scripts */
addReplyErrorFormat(c,"Unknown lua shebang option: %s", parts[j]);
if (err)
*err = sdscatfmt(sdsempty(), "Unknown lua shebang option: %s", parts[j]);
sdsfreesplitres(parts, numparts);
return NULL;
return C_ERR;
}
}
sdsfreesplitres(parts, numparts);
}
if (out_shebang_len)
*out_shebang_len = shebang_len;
*out_flags = script_flags;
return C_OK;
}
/* Try to extract command flags if we can, returns the modified flags.
* Note that it does not guarantee the command arguments are right. */
uint64_t evalGetCommandFlags(client *c, uint64_t cmd_flags) {
char funcname[43];
int evalsha = c->cmd->proc == evalShaCommand || c->cmd->proc == evalShaRoCommand;
if (evalsha && sdslen(c->argv[1]->ptr) != 40)
return cmd_flags;
evalCalcFunctionName(evalsha, c->argv[1]->ptr, funcname);
char *lua_cur_script = funcname + 2;
dictEntry *de = dictFind(lctx.lua_scripts, lua_cur_script);
uint64_t script_flags;
if (!de) {
if (evalsha)
return cmd_flags;
if (evalExtractShebangFlags(c->argv[1]->ptr, &script_flags, NULL, NULL) == C_ERR)
return cmd_flags;
} else {
luaScript *l = dictGetVal(de);
script_flags = l->flags;
}
if (script_flags & SCRIPT_FLAG_EVAL_COMPAT_MODE)
return cmd_flags;
return scriptFlagsToCmdFlags(cmd_flags, script_flags);
}
/* Define a Lua function with the specified body.
* The function name will be generated in the following form:
*
* f_<hex sha1 sum>
*
* The function increments the reference count of the 'body' object as a
* side effect of a successful call.
*
* On success a pointer to an SDS string representing the function SHA1 of the
* just added function is returned (and will be valid until the next call
* to scriptingReset() function), otherwise NULL is returned.
*
* The function handles the fact of being called with a script that already
* exists, and in such a case, it behaves like in the success case.
*
* If 'c' is not NULL, on error the client is informed with an appropriate
* error describing the nature of the problem and the Lua interpreter error. */
sds luaCreateFunction(client *c, robj *body) {
char funcname[43];
dictEntry *de;
uint64_t script_flags;
funcname[0] = 'f';
funcname[1] = '_';
sha1hex(funcname+2,body->ptr,sdslen(body->ptr));
if ((de = dictFind(lctx.lua_scripts,funcname+2)) != NULL) {
return dictGetKey(de);
}
/* Handle shebang header in script code */
ssize_t shebang_len = 0;
sds err = NULL;
if (evalExtractShebangFlags(body->ptr, &script_flags, &shebang_len, &err) == C_ERR) {
addReplyErrorSds(c, err);
return NULL;
}
/* Note that in case of a shebang line we skip it but keep the line feed to conserve the user's line numbers */
if (luaL_loadbuffer(lctx.lua,(char*)body->ptr + shebang_len,sdslen(body->ptr) - shebang_len,"@user_script")) {
......@@ -430,26 +502,7 @@ void evalGenericCommand(client *c, int evalsha) {
return;
}
/* We obtain the script SHA1, then check if this function is already
* defined into the Lua state */
funcname[0] = 'f';
funcname[1] = '_';
if (!evalsha) {
/* Hash the code if this is an EVAL call */
sha1hex(funcname+2,c->argv[1]->ptr,sdslen(c->argv[1]->ptr));
} else {
/* We already have the SHA if it is an EVALSHA */
int j;
char *sha = c->argv[1]->ptr;
/* Convert to lowercase. We don't use tolower since the function
* managed to always show up in the profiler output consuming
* a non trivial amount of time. */
for (j = 0; j < 40; j++)
funcname[j+2] = (sha[j] >= 'A' && sha[j] <= 'Z') ?
sha[j]+('a'-'A') : sha[j];
funcname[42] = '\0';
}
evalCalcFunctionName(evalsha, c->argv[1]->ptr, funcname);
/* Push the pcall error handler function on the stack. */
lua_getglobal(lua, "__redis__err__handler");
......
......@@ -481,7 +481,7 @@ void startEvictionTimeProc(void) {
static int isSafeToPerformEvictions(void) {
/* - There must be no script in timeout condition.
* - Nor we are loading data right now. */
if (scriptIsTimedout() || server.loading) return 0;
if (isInsideYieldingLongCommand() || server.loading) return 0;
/* By default replicas should ignore maxmemory
* and just be masters exact copies. */
......
......@@ -662,22 +662,22 @@ void expireGenericCommand(client *c, long long basetime, int unit) {
}
}
/* EXPIRE key seconds */
/* EXPIRE key seconds [ NX | XX | GT | LT] */
void expireCommand(client *c) {
expireGenericCommand(c,mstime(),UNIT_SECONDS);
}
/* EXPIREAT key time */
/* EXPIREAT key unix-time-seconds [ NX | XX | GT | LT] */
void expireatCommand(client *c) {
expireGenericCommand(c,0,UNIT_SECONDS);
}
/* PEXPIRE key milliseconds */
/* PEXPIRE key milliseconds [ NX | XX | GT | LT] */
void pexpireCommand(client *c) {
expireGenericCommand(c,mstime(),UNIT_MILLISECONDS);
}
/* PEXPIREAT key ms_time */
/* PEXPIREAT key unix-time-milliseconds [ NX | XX | GT | LT] */
void pexpireatCommand(client *c) {
expireGenericCommand(c,0,UNIT_MILLISECONDS);
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment