Add script function flags in the module API (#2836)

This commit adds script function flags to the module API, which allows
function scripts to specify the function flags programmatically.

When the scripting engine compiles the script code can extract the flags
from the code and set the flags on the compiled function objects.

---------

Signed-off-by: Ricardo Dias <ricardo.dias@percona.com>
This commit is contained in:
Ricardo Dias 2025-11-20 10:23:00 +00:00 committed by GitHub
parent ed8856bdfc
commit 05540af405
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 38 additions and 11 deletions

View File

@ -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 */
};

View File

@ -92,7 +92,7 @@ struct scriptRunCtx {
/* Defines a script flags */
typedef struct scriptFlag {
uint64_t flag;
ValkeyModuleScriptingEngineScriptFlag flag;
const char *str;
} scriptFlag;

View File

@ -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;

View File

@ -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;

View File

@ -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 {}}}}}