summaryrefslogtreecommitdiff
path: root/src/functions.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/functions.c')
-rw-r--r--src/functions.c168
1 files changed, 113 insertions, 55 deletions
diff --git a/src/functions.c b/src/functions.c
index 739d178aa..d327d3358 100644
--- a/src/functions.c
+++ b/src/functions.c
@@ -57,6 +57,12 @@ struct functionsLibCtx {
dict *engines_stats; /* Per engine statistics */
};
+typedef struct functionsLibMataData {
+ sds engine;
+ sds name;
+ sds code;
+} functionsLibMataData;
+
dictType engineDictType = {
dictSdsCaseHash, /* hash function */
dictSdsDup, /* key dup */
@@ -124,7 +130,6 @@ static size_t functionMallocSize(functionInfo *fi) {
static size_t libraryMallocSize(functionLibInfo *li) {
return zmalloc_size(li) + sdsZmallocSize(li->name)
- + (li->desc ? sdsZmallocSize(li->desc) : 0)
+ sdsZmallocSize(li->code);
}
@@ -157,7 +162,6 @@ static void engineLibraryFree(functionLibInfo* li) {
dictRelease(li->functions);
sdsfree(li->name);
sdsfree(li->code);
- if (li->desc) sdsfree(li->desc);
zfree(li);
}
@@ -265,14 +269,13 @@ int functionLibCreateFunction(sds name, void *function, functionLibInfo *li, sds
return C_OK;
}
-static functionLibInfo* engineLibraryCreate(sds name, engineInfo *ei, sds desc, sds code) {
+static functionLibInfo* engineLibraryCreate(sds name, engineInfo *ei, sds code) {
functionLibInfo *li = zmalloc(sizeof(*li));
*li = (functionLibInfo) {
.name = sdsdup(name),
.functions = dictCreate(&libraryFunctionDictType),
.ei = ei,
.code = sdsdup(code),
- .desc = desc ? sdsdup(desc) : NULL,
};
return li;
}
@@ -540,17 +543,11 @@ void functionListCommand(client *c) {
}
}
++reply_len;
- addReplyMapLen(c, with_code? 5 : 4);
+ addReplyMapLen(c, with_code? 4 : 3);
addReplyBulkCString(c, "library_name");
addReplyBulkCBuffer(c, li->name, sdslen(li->name));
addReplyBulkCString(c, "engine");
addReplyBulkCBuffer(c, li->ei->name, sdslen(li->ei->name));
- addReplyBulkCString(c, "description");
- if (li->desc) {
- addReplyBulkCBuffer(c, li->desc, sdslen(li->desc));
- } else {
- addReplyNull(c);
- }
addReplyBulkCString(c, "functions");
addReplyArrayLen(c, dictSize(li->functions));
@@ -745,11 +742,11 @@ void functionRestoreCommand(client *c) {
err = sdsnew("can not read data type");
goto load_error;
}
- if (type != RDB_OPCODE_FUNCTION) {
+ if (type != RDB_OPCODE_FUNCTION && type != RDB_OPCODE_FUNCTION2) {
err = sdsnew("given type is not a function");
goto load_error;
}
- if (rdbFunctionLoad(&payload, rdbver, functions_lib_ctx, RDBFLAGS_NONE, &err) != C_OK) {
+ if (rdbFunctionLoad(&payload, rdbver, functions_lib_ctx, type, RDBFLAGS_NONE, &err) != C_OK) {
if (!err) {
err = sdsnew("failed loading the given functions payload");
}
@@ -868,36 +865,111 @@ static int functionsVerifyName(sds name) {
return C_OK;
}
-/* Compile and save the given library, return C_OK on success and C_ERR on failure.
- * In case on failure the err out param is set with relevant error message */
-int functionsCreateWithLibraryCtx(sds lib_name,sds engine_name, sds desc, sds code,
- int replace, sds* err, functionsLibCtx *lib_ctx) {
+int functionExtractLibMetaData(sds payload, functionsLibMataData *md, sds *err) {
+ sds name = NULL;
+ sds desc = NULL;
+ sds engine = NULL;
+ sds code = NULL;
+ if (strncmp(payload, "#!", 2) != 0) {
+ *err = sdsnew("Missing library metadata");
+ return C_ERR;
+ }
+ char *shebang_end = strchr(payload, '\n');
+ if (shebang_end == NULL) {
+ *err = sdsnew("Invalid library metadata");
+ return C_ERR;
+ }
+ size_t shebang_len = shebang_end - payload;
+ sds shebang = sdsnewlen(payload, shebang_len);
+ int numparts;
+ sds *parts = sdssplitargs(shebang, &numparts);
+ sdsfree(shebang);
+ if (!parts || numparts == 0) {
+ *err = sdsnew("Invalid library metadata");
+ sdsfreesplitres(parts, numparts);
+ return C_ERR;
+ }
+ engine = sdsdup(parts[0]);
+ sdsrange(engine, 2, -1);
+ for (int i = 1 ; i < numparts ; ++i) {
+ sds part = parts[i];
+ if (strncasecmp(part, "name=", 5) == 0) {
+ if (name) {
+ *err = sdscatfmt(sdsempty(), "Invalid metadata value, name argument was given multiple times");
+ goto error;
+ }
+ name = sdsdup(part);
+ sdsrange(name, 5, -1);
+ continue;
+ }
+ *err = sdscatfmt(sdsempty(), "Invalid metadata value given: %s", part);
+ goto error;
+ }
+
+ if (!name) {
+ *err = sdsnew("Library name was not given");
+ goto error;
+ }
+
+ sdsfreesplitres(parts, numparts);
+
+ md->name = name;
+ md->code = sdsnewlen(shebang_end, sdslen(payload) - shebang_len);
+ md->engine = engine;
+
+ return C_OK;
+
+error:
+ if (name) sdsfree(name);
+ if (desc) sdsfree(desc);
+ if (engine) sdsfree(engine);
+ if (code) sdsfree(code);
+ sdsfreesplitres(parts, numparts);
+ return C_ERR;
+}
+
+void functionFreeLibMetaData(functionsLibMataData *md) {
+ if (md->code) sdsfree(md->code);
+ if (md->name) sdsfree(md->name);
+ if (md->engine) sdsfree(md->engine);
+}
+
+/* Compile and save the given library, return the loaded library name on success
+ * and NULL on failure. In case on failure the err out param is set with relevant error message */
+sds functionsCreateWithLibraryCtx(sds code, int replace, sds* err, functionsLibCtx *lib_ctx) {
dictIterator *iter = NULL;
dictEntry *entry = NULL;
- if (functionsVerifyName(lib_name)) {
+ functionLibInfo *new_li = NULL;
+ functionLibInfo *old_li = NULL;
+ functionsLibMataData md = {0};
+ if (functionExtractLibMetaData(code, &md, err) != C_OK) {
+ return NULL;
+ }
+
+ if (functionsVerifyName(md.name)) {
*err = sdsnew("Library names can only contain letters and numbers and must be at least one character long");
- return C_ERR;
+ goto error;
}
- engineInfo *ei = dictFetchValue(engines, engine_name);
+ engineInfo *ei = dictFetchValue(engines, md.engine);
if (!ei) {
- *err = sdsnew("Engine not found");
- return C_ERR;
+ *err = sdscatfmt(sdsempty(), "Engine '%S' not found", md.engine);
+ goto error;
}
engine *engine = ei->engine;
- functionLibInfo *old_li = dictFetchValue(lib_ctx->libraries, lib_name);
+ old_li = dictFetchValue(lib_ctx->libraries, md.name);
if (old_li && !replace) {
- *err = sdsnew("Library already exists");
- return C_ERR;
+ *err = sdscatfmt(sdsempty(), "Library '%S' already exists", md.name);
+ goto error;
}
if (old_li) {
libraryUnlink(lib_ctx, old_li);
}
- functionLibInfo *new_li = engineLibraryCreate(lib_name, ei, desc, code);
- if (engine->create(engine->engine_ctx, new_li, code, err) != C_OK) {
+ new_li = engineLibraryCreate(md.name, ei, code);
+ if (engine->create(engine->engine_ctx, new_li, md.code, err) != C_OK) {
goto error;
}
@@ -925,48 +997,34 @@ int functionsCreateWithLibraryCtx(sds lib_name,sds engine_name, sds desc, sds co
engineLibraryFree(old_li);
}
- return C_OK;
+ sds loaded_lib_name = md.name;
+ md.name = NULL;
+ functionFreeLibMetaData(&md);
+
+ return loaded_lib_name;
error:
if (iter) dictReleaseIterator(iter);
- engineLibraryFree(new_li);
- if (old_li) {
- libraryLink(lib_ctx, old_li);
- }
- return C_ERR;
+ if (new_li) engineLibraryFree(new_li);
+ if (old_li) libraryLink(lib_ctx, old_li);
+ functionFreeLibMetaData(&md);
+ return NULL;
}
/*
- * FUNCTION LOAD <ENGINE NAME> <LIBRARY NAME>
- * [REPLACE] [DESC <LIBRARY DESCRIPTION>] <LIBRARY CODE>
- *
- * ENGINE NAME - name of the engine to use the run the library
- * LIBRARY NAME - name of the library
+ * FUNCTION LOAD [REPLACE] <LIBRARY CODE>
* REPLACE - optional, replace existing library
- * DESCRIPTION - optional, library description
* LIBRARY CODE - library code to pass to the engine
*/
void functionLoadCommand(client *c) {
- robj *engine_name = c->argv[2];
- robj *library_name = c->argv[3];
-
int replace = 0;
- int argc_pos = 4;
- sds desc = NULL;
+ int argc_pos = 2;
while (argc_pos < c->argc - 1) {
robj *next_arg = c->argv[argc_pos++];
if (!strcasecmp(next_arg->ptr, "replace")) {
replace = 1;
continue;
}
- if (!strcasecmp(next_arg->ptr, "description")) {
- if (argc_pos >= c->argc) {
- addReplyError(c, "Bad function description");
- return;
- }
- desc = c->argv[argc_pos++]->ptr;
- continue;
- }
addReplyErrorFormat(c, "Unknown option given: %s", (char*)next_arg->ptr);
return;
}
@@ -978,8 +1036,8 @@ void functionLoadCommand(client *c) {
robj *code = c->argv[argc_pos];
sds err = NULL;
- if (functionsCreateWithLibraryCtx(library_name->ptr, engine_name->ptr,
- desc, code->ptr, replace, &err, curr_functions_lib_ctx) != C_OK)
+ sds library_name = NULL;
+ if (!(library_name = functionsCreateWithLibraryCtx(code->ptr, replace, &err, curr_functions_lib_ctx)))
{
addReplyErrorSds(c, err);
return;
@@ -987,7 +1045,7 @@ void functionLoadCommand(client *c) {
/* Indicate that the command changed the data so it will be replicated and
* counted as a data change (for persistence configuration) */
server.dirty++;
- addReply(c, shared.ok);
+ addReplyBulkSds(c, library_name);
}
/* Return memory usage of all the engines combine */