From ffafb434fb50b5697d1aa35342721eaa5f13e0eb Mon Sep 17 00:00:00 2001 From: Hanna Fadida Date: Thu, 30 Sep 2021 11:21:32 +0300 Subject: [PATCH] Modules: add RM_LoadDataTypeFromStringEncver (#9537) adding an advanced api to enable loading data that was sereialized with a specific encoding version --- src/module.c | 16 ++++++++++++---- src/redismodule.h | 2 ++ tests/modules/datatype.c | 22 +++++++++++++++------- tests/unit/moduleapi/datatype.tcl | 8 ++++---- 4 files changed, 33 insertions(+), 15 deletions(-) diff --git a/src/module.c b/src/module.c index eeda8573d..a5d5afe40 100644 --- a/src/module.c +++ b/src/module.c @@ -5722,8 +5722,8 @@ void RM_DigestEndSequence(RedisModuleDigest *md) { memset(md->o,0,sizeof(md->o)); } -/* Decode a serialized representation of a module data type 'mt' from string - * 'str' and return a newly allocated value, or NULL if decoding failed. +/* Decode a serialized representation of a module data type 'mt', in a specific encoding version 'encver' + * from string 'str' and return a newly allocated value, or NULL if decoding failed. * * This call basically reuses the 'rdb_load' callback which module data types * implement in order to allow a module to arbitrarily serialize/de-serialize @@ -5736,7 +5736,7 @@ void RM_DigestEndSequence(RedisModuleDigest *md) { * If this is NOT done, Redis will handle corrupted (or just truncated) serialized * data by producing an error message and terminating the process. */ -void *RM_LoadDataTypeFromString(const RedisModuleString *str, const moduleType *mt) { +void *RM_LoadDataTypeFromStringEncver(const RedisModuleString *str, const moduleType *mt, int encver) { rio payload; RedisModuleIO io; void *ret; @@ -5748,7 +5748,7 @@ void *RM_LoadDataTypeFromString(const RedisModuleString *str, const moduleType * * need to make sure we read the same. */ io.ver = 2; - ret = mt->rdb_load(&io,0); + ret = mt->rdb_load(&io,encver); if (io.ctx) { moduleFreeContext(io.ctx); zfree(io.ctx); @@ -5756,6 +5756,13 @@ void *RM_LoadDataTypeFromString(const RedisModuleString *str, const moduleType * return ret; } +/* Similar to RM_LoadDataTypeFromStringEncver, original version of the API, kept + * for backward compatibility. + */ +void *RM_LoadDataTypeFromString(const RedisModuleString *str, const moduleType *mt) { + return RM_LoadDataTypeFromStringEncver(str, mt, 0); +} + /* Encode a module data type 'mt' value 'data' into serialized form, and return it * as a newly allocated RedisModuleString. * @@ -10352,6 +10359,7 @@ void moduleRegisterCoreAPI(void) { REGISTER_API(LoadLongDouble); REGISTER_API(SaveDataTypeToString); REGISTER_API(LoadDataTypeFromString); + REGISTER_API(LoadDataTypeFromStringEncver); REGISTER_API(EmitAOF); REGISTER_API(Log); REGISTER_API(LogIOError); diff --git a/src/redismodule.h b/src/redismodule.h index acfa72d0a..586b0c6ee 100644 --- a/src/redismodule.h +++ b/src/redismodule.h @@ -746,6 +746,7 @@ REDISMODULE_API float (*RedisModule_LoadFloat)(RedisModuleIO *io) REDISMODULE_AT REDISMODULE_API void (*RedisModule_SaveLongDouble)(RedisModuleIO *io, long double value) REDISMODULE_ATTR; REDISMODULE_API long double (*RedisModule_LoadLongDouble)(RedisModuleIO *io) REDISMODULE_ATTR; REDISMODULE_API void * (*RedisModule_LoadDataTypeFromString)(const RedisModuleString *str, const RedisModuleType *mt) REDISMODULE_ATTR; +REDISMODULE_API void * (*RedisModule_LoadDataTypeFromStringEncver)(const RedisModuleString *str, const RedisModuleType *mt, int encver) REDISMODULE_ATTR; REDISMODULE_API RedisModuleString * (*RedisModule_SaveDataTypeToString)(RedisModuleCtx *ctx, void *data, const RedisModuleType *mt) REDISMODULE_ATTR; REDISMODULE_API void (*RedisModule_Log)(RedisModuleCtx *ctx, const char *level, const char *fmt, ...) REDISMODULE_ATTR REDISMODULE_ATTR_PRINTF(3,4); REDISMODULE_API void (*RedisModule_LogIOError)(RedisModuleIO *io, const char *levelstr, const char *fmt, ...) REDISMODULE_ATTR REDISMODULE_ATTR_PRINTF(3,4); @@ -1062,6 +1063,7 @@ static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int REDISMODULE_GET_API(LoadLongDouble); REDISMODULE_GET_API(SaveDataTypeToString); REDISMODULE_GET_API(LoadDataTypeFromString); + REDISMODULE_GET_API(LoadDataTypeFromStringEncver); REDISMODULE_GET_API(EmitAOF); REDISMODULE_GET_API(Log); REDISMODULE_GET_API(LogIOError); diff --git a/tests/modules/datatype.c b/tests/modules/datatype.c index 0c6f95551..8a70d4be1 100644 --- a/tests/modules/datatype.c +++ b/tests/modules/datatype.c @@ -5,6 +5,9 @@ #include "redismodule.h" static RedisModuleType *datatype = NULL; +static int load_encver = 0; + +#define DATATYPE_ENC_VER 1 typedef struct { long long intval; @@ -12,8 +15,7 @@ typedef struct { } DataType; static void *datatype_load(RedisModuleIO *io, int encver) { - (void) encver; - + load_encver = encver; int intval = RedisModule_LoadSigned(io); if (RedisModule_IsIOError(io)) return NULL; @@ -76,7 +78,7 @@ static int datatype_set(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) long long intval; if (RedisModule_StringToLongLong(argv[2], &intval) != REDISMODULE_OK) { - RedisModule_ReplyWithError(ctx, "Invalid integr value"); + RedisModule_ReplyWithError(ctx, "Invalid integer value"); return REDISMODULE_OK; } @@ -94,12 +96,18 @@ static int datatype_set(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) } static int datatype_restore(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - if (argc != 3) { + if (argc != 4) { RedisModule_WrongArity(ctx); return REDISMODULE_OK; } - DataType *dt = RedisModule_LoadDataTypeFromString(argv[2], datatype); + long long encver; + if (RedisModule_StringToLongLong(argv[3], &encver) != REDISMODULE_OK) { + RedisModule_ReplyWithError(ctx, "Invalid integer value"); + return REDISMODULE_OK; + } + + DataType *dt = RedisModule_LoadDataTypeFromStringEncver(argv[2], datatype, encver); if (!dt) { RedisModule_ReplyWithError(ctx, "Invalid data"); return REDISMODULE_OK; @@ -108,7 +116,7 @@ static int datatype_restore(RedisModuleCtx *ctx, RedisModuleString **argv, int a RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], REDISMODULE_WRITE); RedisModule_ModuleTypeSetValue(key, datatype, dt); RedisModule_CloseKey(key); - RedisModule_ReplyWithSimpleString(ctx, "OK"); + RedisModule_ReplyWithLongLong(ctx, load_encver); return REDISMODULE_OK; } @@ -181,7 +189,7 @@ int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) REDISMODULE_NOT_USED(argv); REDISMODULE_NOT_USED(argc); - if (RedisModule_Init(ctx,"datatype",1,REDISMODULE_APIVER_1) == REDISMODULE_ERR) + if (RedisModule_Init(ctx,"datatype",DATATYPE_ENC_VER,REDISMODULE_APIVER_1) == REDISMODULE_ERR) return REDISMODULE_ERR; RedisModule_SetModuleOptions(ctx, REDISMODULE_OPTIONS_HANDLE_IO_ERRORS); diff --git a/tests/unit/moduleapi/datatype.tcl b/tests/unit/moduleapi/datatype.tcl index cd6ebb32a..0cc687e89 100644 --- a/tests/unit/moduleapi/datatype.tcl +++ b/tests/unit/moduleapi/datatype.tcl @@ -8,20 +8,20 @@ start_server {tags {"modules"}} { assert {[r datatype.get dtkey] eq {100 stringval}} } - test {DataType: RM_SaveDataTypeToString(), RM_LoadDataTypeFromString() work} { + test {DataType: RM_SaveDataTypeToString(), RM_LoadDataTypeFromStringEncver() work} { r datatype.set dtkey -1111 MyString set encoded [r datatype.dump dtkey] - r datatype.restore dtkeycopy $encoded + assert {[r datatype.restore dtkeycopy $encoded 4] eq {4}} assert {[r datatype.get dtkeycopy] eq {-1111 MyString}} } - test {DataType: Handle truncated RM_LoadDataTypeFromString()} { + test {DataType: Handle truncated RM_LoadDataTypeFromStringEncver()} { r datatype.set dtkey -1111 MyString set encoded [r datatype.dump dtkey] set truncated [string range $encoded 0 end-1] - catch {r datatype.restore dtkeycopy $truncated} e + catch {r datatype.restore dtkeycopy $truncated 4} e set e } {*Invalid*}