diff --git a/src/module.c b/src/module.c index 0e19c11e2..c48b2f045 100644 --- a/src/module.c +++ b/src/module.c @@ -43,9 +43,10 @@ typedef struct RedisModuleInfoCtx { struct RedisModule *module; sds requested_section; - sds info; /* info string we collected so far */ - int sections; /* number of sections we collected so far */ - int in_section; /* indication if we're in an active section or not */ + sds info; /* info string we collected so far */ + int sections; /* number of sections we collected so far */ + int in_section; /* indication if we're in an active section or not */ + int in_dict_field; /* indication that we're curreintly appending to a dict */ } RedisModuleInfoCtx; typedef void (*RedisModuleInfoFunc)(RedisModuleInfoCtx *ctx, int for_crash_report); @@ -4704,12 +4705,18 @@ int RM_DictCompare(RedisModuleDictIter *di, const char *op, RedisModuleString *k * Modules Info fields * -------------------------------------------------------------------------- */ +int RM_InfoEndDictField(RedisModuleInfoCtx *ctx); + /* Used to start a new section, before adding any fields. the section name will * be prefixed by "_" and must only include A-Z,a-z,0-9. * When return value is REDISMODULE_ERR, the section should and will be skipped. */ -int RM_AddInfoSection(RedisModuleInfoCtx *ctx, char *name) { +int RM_InfoAddSection(RedisModuleInfoCtx *ctx, char *name) { sds full_name = sdscatprintf(sdsdup(ctx->module->name), "_%s", name); + /* Implicitly end dicts, instead of returning an error which is likely un checked. */ + if (ctx->in_dict_field) + RM_InfoEndDictField(ctx); + /* proceed only if: * 1) no section was requested (emit all) * 2) the module name was requested (emit all) @@ -4729,12 +4736,48 @@ int RM_AddInfoSection(RedisModuleInfoCtx *ctx, char *name) { return REDISMODULE_OK; } +/* Starts a dict field, similar to the ones in INFO KEYSPACE. Use normal + * RedisModule_InfoAddField* functions to add the items to this field, and + * terminate with RedisModule_InfoEndDictField. */ +int RM_InfoBeginDictField(RedisModuleInfoCtx *ctx, char *name) { + if (!ctx->in_section) + return REDISMODULE_ERR; + /* Implicitly end dicts, instead of returning an error which is likely un checked. */ + if (ctx->in_dict_field) + RM_InfoEndDictField(ctx); + ctx->info = sdscatprintf(ctx->info, + "%s_%s:", + ctx->module->name, + name); + ctx->in_dict_field = 1; + return REDISMODULE_OK; +} + +/* Ends a dict field, see RedisModule_InfoBeginDictField */ +int RM_InfoEndDictField(RedisModuleInfoCtx *ctx) { + if (!ctx->in_dict_field) + return REDISMODULE_ERR; + /* trim the last ',' if found. */ + if (ctx->info[sdslen(ctx->info)-1]==',') + sdsIncrLen(ctx->info, -1); + ctx->info = sdscatprintf(ctx->info, "\r\n"); + ctx->in_dict_field = 0; + return REDISMODULE_OK; +} + /* Used by RedisModuleInfoFunc to add info fields. * Each field will be automatically prefixed by "_". * Field names or values must not include \r\n of ":" */ -int RM_AddInfoFieldString(RedisModuleInfoCtx *ctx, char *field, RedisModuleString *value) { +int RM_InfoAddFieldString(RedisModuleInfoCtx *ctx, char *field, RedisModuleString *value) { if (!ctx->in_section) return REDISMODULE_ERR; + if (ctx->in_dict_field) { + ctx->info = sdscatprintf(ctx->info, + "%s=%s,", + field, + (sds)value->ptr); + return REDISMODULE_OK; + } ctx->info = sdscatprintf(ctx->info, "%s_%s:%s\r\n", ctx->module->name, @@ -4743,9 +4786,16 @@ int RM_AddInfoFieldString(RedisModuleInfoCtx *ctx, char *field, RedisModuleStrin return REDISMODULE_OK; } -int RM_AddInfoFieldCString(RedisModuleInfoCtx *ctx, char *field, char *value) { +int RM_InfoAddFieldCString(RedisModuleInfoCtx *ctx, char *field, char *value) { if (!ctx->in_section) return REDISMODULE_ERR; + if (ctx->in_dict_field) { + ctx->info = sdscatprintf(ctx->info, + "%s=%s,", + field, + value); + return REDISMODULE_OK; + } ctx->info = sdscatprintf(ctx->info, "%s_%s:%s\r\n", ctx->module->name, @@ -4754,9 +4804,16 @@ int RM_AddInfoFieldCString(RedisModuleInfoCtx *ctx, char *field, char *value) { return REDISMODULE_OK; } -int RM_AddInfoFieldDouble(RedisModuleInfoCtx *ctx, char *field, double value) { +int RM_InfoAddFieldDouble(RedisModuleInfoCtx *ctx, char *field, double value) { if (!ctx->in_section) return REDISMODULE_ERR; + if (ctx->in_dict_field) { + ctx->info = sdscatprintf(ctx->info, + "%s=%.17g,", + field, + value); + return REDISMODULE_OK; + } ctx->info = sdscatprintf(ctx->info, "%s_%s:%.17g\r\n", ctx->module->name, @@ -4765,9 +4822,16 @@ int RM_AddInfoFieldDouble(RedisModuleInfoCtx *ctx, char *field, double value) { return REDISMODULE_OK; } -int RM_AddInfoFieldLongLong(RedisModuleInfoCtx *ctx, char *field, long long value) { +int RM_InfoAddFieldLongLong(RedisModuleInfoCtx *ctx, char *field, long long value) { if (!ctx->in_section) return REDISMODULE_ERR; + if (ctx->in_dict_field) { + ctx->info = sdscatprintf(ctx->info, + "%s=%lld,", + field, + value); + return REDISMODULE_OK; + } ctx->info = sdscatprintf(ctx->info, "%s_%s:%lld\r\n", ctx->module->name, @@ -4776,9 +4840,16 @@ int RM_AddInfoFieldLongLong(RedisModuleInfoCtx *ctx, char *field, long long valu return REDISMODULE_OK; } -int RM_AddInfoFieldULongLong(RedisModuleInfoCtx *ctx, char *field, unsigned long long value) { +int RM_InfoAddFieldULongLong(RedisModuleInfoCtx *ctx, char *field, unsigned long long value) { if (!ctx->in_section) return REDISMODULE_ERR; + if (ctx->in_dict_field) { + ctx->info = sdscatprintf(ctx->info, + "%s=%llu,", + field, + value); + return REDISMODULE_OK; + } ctx->info = sdscatprintf(ctx->info, "%s_%s:%llu\r\n", ctx->module->name, @@ -4802,6 +4873,9 @@ sds modulesCollectInfo(sds info, sds section, int for_crash_report, int sections continue; RedisModuleInfoCtx info_ctx = {module, section, info, sections, 0}; module->info_cb(&info_ctx, for_crash_report); + /* Implicitly end dicts (no way to handle errors, and we must add the newline). */ + if (info_ctx.in_dict_field) + RM_InfoEndDictField(&info_ctx); info = info_ctx.info; sections = info_ctx.sections; } @@ -5614,10 +5688,12 @@ void moduleRegisterCoreAPI(void) { REGISTER_API(CommandFilterArgReplace); REGISTER_API(CommandFilterArgDelete); REGISTER_API(RegisterInfoFunc); - REGISTER_API(AddInfoSection); - REGISTER_API(AddInfoFieldString); - REGISTER_API(AddInfoFieldCString); - REGISTER_API(AddInfoFieldDouble); - REGISTER_API(AddInfoFieldLongLong); - REGISTER_API(AddInfoFieldULongLong); + REGISTER_API(InfoAddSection); + REGISTER_API(InfoBeginDictField); + REGISTER_API(InfoEndDictField); + REGISTER_API(InfoAddFieldString); + REGISTER_API(InfoAddFieldCString); + REGISTER_API(InfoAddFieldDouble); + REGISTER_API(InfoAddFieldLongLong); + REGISTER_API(InfoAddFieldULongLong); } diff --git a/src/redismodule.h b/src/redismodule.h index 1feb14a68..26690c17c 100644 --- a/src/redismodule.h +++ b/src/redismodule.h @@ -320,12 +320,14 @@ RedisModuleString *REDISMODULE_API_FUNC(RedisModule_DictPrev)(RedisModuleCtx *ct int REDISMODULE_API_FUNC(RedisModule_DictCompareC)(RedisModuleDictIter *di, const char *op, void *key, size_t keylen); int REDISMODULE_API_FUNC(RedisModule_DictCompare)(RedisModuleDictIter *di, const char *op, RedisModuleString *key); int REDISMODULE_API_FUNC(RedisModule_RegisterInfoFunc)(RedisModuleCtx *ctx, RedisModuleInfoFunc cb); -int REDISMODULE_API_FUNC(RedisModule_AddInfoSection)(RedisModuleInfoCtx *ctx, char *name); -int REDISMODULE_API_FUNC(RedisModule_AddInfoFieldString)(RedisModuleInfoCtx *ctx, char *field, RedisModuleString *value); -int REDISMODULE_API_FUNC(RedisModule_AddInfoFieldCString)(RedisModuleInfoCtx *ctx, char *field, char *value); -int REDISMODULE_API_FUNC(RedisModule_AddInfoFieldDouble)(RedisModuleInfoCtx *ctx, char *field, double value); -int REDISMODULE_API_FUNC(RedisModule_AddInfoFieldLongLong)(RedisModuleInfoCtx *ctx, char *field, long long value); -int REDISMODULE_API_FUNC(RedisModule_AddInfoFieldULongLong)(RedisModuleInfoCtx *ctx, char *field, unsigned long long value); +int REDISMODULE_API_FUNC(RedisModule_InfoAddSection)(RedisModuleInfoCtx *ctx, char *name); +int REDISMODULE_API_FUNC(RedisModule_InfoBeginDictField)(RedisModuleInfoCtx *ctx, char *name); +int REDISMODULE_API_FUNC(RedisModule_InfoEndDictField)(RedisModuleInfoCtx *ctx); +int REDISMODULE_API_FUNC(RedisModule_InfoAddFieldString)(RedisModuleInfoCtx *ctx, char *field, RedisModuleString *value); +int REDISMODULE_API_FUNC(RedisModule_InfoAddFieldCString)(RedisModuleInfoCtx *ctx, char *field, char *value); +int REDISMODULE_API_FUNC(RedisModule_InfoAddFieldDouble)(RedisModuleInfoCtx *ctx, char *field, double value); +int REDISMODULE_API_FUNC(RedisModule_InfoAddFieldLongLong)(RedisModuleInfoCtx *ctx, char *field, long long value); +int REDISMODULE_API_FUNC(RedisModule_InfoAddFieldULongLong)(RedisModuleInfoCtx *ctx, char *field, unsigned long long value); /* Experimental APIs */ #ifdef REDISMODULE_EXPERIMENTAL_API @@ -500,12 +502,14 @@ static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int REDISMODULE_GET_API(DictCompare); REDISMODULE_GET_API(DictCompareC); REDISMODULE_GET_API(RegisterInfoFunc); - REDISMODULE_GET_API(AddInfoSection); - REDISMODULE_GET_API(AddInfoFieldString); - REDISMODULE_GET_API(AddInfoFieldCString); - REDISMODULE_GET_API(AddInfoFieldDouble); - REDISMODULE_GET_API(AddInfoFieldLongLong); - REDISMODULE_GET_API(AddInfoFieldULongLong); + REDISMODULE_GET_API(InfoAddSection); + REDISMODULE_GET_API(InfoBeginDictField); + REDISMODULE_GET_API(InfoEndDictField); + REDISMODULE_GET_API(InfoAddFieldString); + REDISMODULE_GET_API(InfoAddFieldCString); + REDISMODULE_GET_API(InfoAddFieldDouble); + REDISMODULE_GET_API(InfoAddFieldLongLong); + REDISMODULE_GET_API(InfoAddFieldULongLong); #ifdef REDISMODULE_EXPERIMENTAL_API REDISMODULE_GET_API(GetThreadSafeContext); diff --git a/tests/modules/infotest.c b/tests/modules/infotest.c index d53ea2126..1a75d1606 100644 --- a/tests/modules/infotest.c +++ b/tests/modules/infotest.c @@ -3,19 +3,25 @@ #include void InfoFunc(RedisModuleInfoCtx *ctx, int for_crash_report) { - RedisModule_AddInfoSection(ctx, "Spanish"); - RedisModule_AddInfoFieldCString(ctx, "uno", "one"); - RedisModule_AddInfoFieldLongLong(ctx, "dos", 2); + RedisModule_InfoAddSection(ctx, "Spanish"); + RedisModule_InfoAddFieldCString(ctx, "uno", "one"); + RedisModule_InfoAddFieldLongLong(ctx, "dos", 2); - RedisModule_AddInfoSection(ctx, "Italian"); - RedisModule_AddInfoFieldLongLong(ctx, "due", 2); - RedisModule_AddInfoFieldDouble(ctx, "tre", 3.3); + RedisModule_InfoAddSection(ctx, "Italian"); + RedisModule_InfoAddFieldLongLong(ctx, "due", 2); + RedisModule_InfoAddFieldDouble(ctx, "tre", 3.3); + + RedisModule_InfoAddSection(ctx, "keyspace"); + RedisModule_InfoBeginDictField(ctx, "db0"); + RedisModule_InfoAddFieldLongLong(ctx, "keys", 3); + RedisModule_InfoAddFieldLongLong(ctx, "expires", 1); + RedisModule_InfoEndDictField(ctx); if (for_crash_report) { - RedisModule_AddInfoSection(ctx, "Klingon"); - RedisModule_AddInfoFieldCString(ctx, "one", "wa’"); - RedisModule_AddInfoFieldCString(ctx, "two", "cha’"); - RedisModule_AddInfoFieldCString(ctx, "three", "wej"); + RedisModule_InfoAddSection(ctx, "Klingon"); + RedisModule_InfoAddFieldCString(ctx, "one", "wa’"); + RedisModule_InfoAddFieldCString(ctx, "two", "cha’"); + RedisModule_InfoAddFieldCString(ctx, "three", "wej"); } } diff --git a/tests/unit/moduleapi/infotest.tcl b/tests/unit/moduleapi/infotest.tcl index 143f9050a..7f756f0e6 100644 --- a/tests/unit/moduleapi/infotest.tcl +++ b/tests/unit/moduleapi/infotest.tcl @@ -48,6 +48,11 @@ start_server {tags {"modules"}} { field $info infotest_uno } {one} - # TODO: test crash report. + test {module info dict} { + set info [r info infotest_keyspace] + set keyspace [field $info infotest_db0] + set keys [scan [regexp -inline {keys\=([\d]*)} $keyspace] keys=%d] + } {3} + # TODO: test crash report. }