diff --git a/src/script.c b/src/script.c index 8ae5355629..2f5c57c063 100644 --- a/src/script.c +++ b/src/script.c @@ -34,11 +34,11 @@ #include "module.h" scriptFlag scripts_flags_def[] = { - {.flag = SCRIPT_FLAG_NO_WRITES, .str = "no-writes"}, - {.flag = SCRIPT_FLAG_ALLOW_OOM, .str = "allow-oom"}, - {.flag = SCRIPT_FLAG_ALLOW_STALE, .str = "allow-stale"}, - {.flag = SCRIPT_FLAG_NO_CLUSTER, .str = "no-cluster"}, - {.flag = SCRIPT_FLAG_ALLOW_CROSS_SLOT, .str = "allow-cross-slot-keys"}, + {.flag = VMSE_SCRIPT_FLAG_NO_WRITES, .str = "no-writes"}, + {.flag = VMSE_SCRIPT_FLAG_ALLOW_OOM, .str = "allow-oom"}, + {.flag = VMSE_SCRIPT_FLAG_ALLOW_STALE, .str = "allow-stale"}, + {.flag = VMSE_SCRIPT_FLAG_NO_CLUSTER, .str = "no-cluster"}, + {.flag = VMSE_SCRIPT_FLAG_ALLOW_CROSS_SLOT, .str = "allow-cross-slot-keys"}, {.flag = 0, .str = NULL}, /* flags array end */ }; diff --git a/src/script.h b/src/script.h index 2f3b2efee1..0d1ae5e5f9 100644 --- a/src/script.h +++ b/src/script.h @@ -92,7 +92,7 @@ struct scriptRunCtx { /* Defines a script flags */ typedef struct scriptFlag { - uint64_t flag; + ValkeyModuleScriptingEngineScriptFlag flag; const char *str; } scriptFlag; diff --git a/src/valkeymodule.h b/src/valkeymodule.h index ee0bcac5a7..b24908b3f7 100644 --- a/src/valkeymodule.h +++ b/src/valkeymodule.h @@ -907,6 +907,15 @@ typedef enum ValkeyModuleScriptingEngineExecutionState { VMSE_STATE_KILLED, } ValkeyModuleScriptingEngineExecutionState; +typedef enum ValkeyModuleScriptingEngineScriptFlag { + VMSE_SCRIPT_FLAG_NO_WRITES = (1ULL << 0), + VMSE_SCRIPT_FLAG_ALLOW_OOM = (1ULL << 1), + VMSE_SCRIPT_FLAG_ALLOW_STALE = (1ULL << 2), + VMSE_SCRIPT_FLAG_NO_CLUSTER = (1ULL << 3), + VMSE_SCRIPT_FLAG_EVAL_COMPAT_MODE = (1ULL << 4), /* EVAL Script backwards compatible behavior, no shebang provided */ + VMSE_SCRIPT_FLAG_ALLOW_CROSS_SLOT = (1ULL << 5), +} ValkeyModuleScriptingEngineScriptFlag; + typedef struct ValkeyModuleScriptingEngineCallableLazyEnvReset { void *context; diff --git a/tests/modules/helloscripting.c b/tests/modules/helloscripting.c index 2beefb736c..9a54496a48 100644 --- a/tests/modules/helloscripting.c +++ b/tests/modules/helloscripting.c @@ -27,7 +27,7 @@ * RETURN # returns the current value on the top of the stack and marks * # the end of the function declaration * - * FUNCTION bar # declaration of function 'bar' + * RFUNCTION bar # declaration of read-only function 'bar' * CONSTI 432 # pushes the value 432 to the top of the stack * RETURN # returns the current value on the top of the stack and marks * # the end of the function declaration. @@ -55,6 +55,7 @@ */ typedef enum HelloInstKind { FUNCTION = 0, + RFUNCTION, CONSTI, CONSTS, ARGS, @@ -69,6 +70,7 @@ typedef enum HelloInstKind { */ const char *HelloInstKindStr[] = { "FUNCTION", + "RFUNCTION", "CONSTI", "CONSTS", "ARGS", @@ -119,6 +121,7 @@ typedef struct HelloFunc { HelloInst instructions[256]; uint32_t num_instructions; uint32_t index; + int read_only; } HelloFunc; /* @@ -207,11 +210,12 @@ static HelloInstKind helloLangParseInstruction(const char *token) { /* * Parses the function param. */ -static void helloLangParseFunction(HelloFunc *func) { +static void helloLangParseFunction(HelloFunc *func, int read_only) { char *token = strtok(NULL, " \n"); ValkeyModule_Assert(token != NULL); func->name = ValkeyModule_Alloc(sizeof(char) * strlen(token) + 1); strcpy(func->name, token); + func->read_only = read_only; } /* @@ -283,12 +287,13 @@ static int helloLangParseCode(const char *code, switch (kind) { case FUNCTION: + case RFUNCTION: ValkeyModule_Assert(currentFunc == NULL); currentFunc = ValkeyModule_Alloc(sizeof(HelloFunc)); memset(currentFunc, 0, sizeof(HelloFunc)); currentFunc->index = program->num_functions; program->functions[program->num_functions++] = currentFunc; - helloLangParseFunction(currentFunc); + helloLangParseFunction(currentFunc, kind == RFUNCTION); break; case CONSTI: ValkeyModule_Assert(currentFunc != NULL); @@ -424,6 +429,7 @@ static void helloDebuggerLogCurrentInstr(uint32_t pc, HelloInst *instr) { msg = ValkeyModule_CreateStringPrintf(NULL, ">>> %3u: %s", pc, HelloInstKindStr[instr->kind]); break; case FUNCTION: + case RFUNCTION: case _NUM_INSTRUCTIONS: ValkeyModule_Assert(0); } @@ -528,6 +534,7 @@ static HelloExecutionState executeHelloLangFunction(ValkeyModuleCtx *module_ctx, return FINISHED; } case FUNCTION: + case RFUNCTION: case _NUM_INSTRUCTIONS: ValkeyModule_Assert(0); } @@ -646,7 +653,7 @@ static ValkeyModuleScriptingEngineCompiledFunction **createHelloLangEngine(Valke .name = ValkeyModule_CreateString(NULL, func->name, strlen(func->name)), .function = func, .desc = NULL, - .f_flags = 0, + .f_flags = func->read_only ? VMSE_SCRIPT_FLAG_NO_WRITES : 0, }; compiled_functions[i] = cfunc; diff --git a/tests/unit/moduleapi/scriptingengine.tcl b/tests/unit/moduleapi/scriptingengine.tcl index 528e58fb52..47d5f96f8a 100644 --- a/tests/unit/moduleapi/scriptingengine.tcl +++ b/tests/unit/moduleapi/scriptingengine.tcl @@ -1,6 +1,6 @@ set testmodule [file normalize tests/modules/helloscripting.so] -set HELLO_PROGRAM "#!hello name=mylib\nFUNCTION foo\nARGS 0\nRETURN\nFUNCTION bar\nCONSTI 432\nRETURN" +set HELLO_PROGRAM "#!hello name=mylib\nRFUNCTION foo\nARGS 0\nRETURN\nFUNCTION bar\nCONSTI 432\nRETURN" start_server {tags {"modules"}} { r module load $testmodule @@ -197,6 +197,16 @@ start_server {tags {"modules"}} { RETURN } 0 } + + r function load {#!hello name=errlib + RFUNCTION callcmd + CONSTS x + CONSTI 43 + CONSTI 2 + CALL SET + RETURN + } + assert_error {ERR Write commands are not allowed*} {r fcall callcmd 0} } test {Call server command when OOM} { @@ -301,6 +311,7 @@ start_server {tags {"modules"}} { } test {List scripting engine functions} { + r function flush sync r function load replace "#!hello name=mylib\nFUNCTION foobar\nARGS 0\nRETURN" r function list } {{library_name mylib engine HELLO functions {{name foobar description {} flags {}}}}}