From 8886398451ae549eacecae1f84976067fd684ec6 Mon Sep 17 00:00:00 2001 From: Attila Kovacs Date: Thu, 5 Sep 2024 10:08:03 +0200 Subject: [PATCH] Add redisxGetStringValue() and site update --- README.md | 12 ++++------- include/redisx.h | 1 + src/redisx-tab.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 55 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 3e59449..afb2f9b 100644 --- a/README.md +++ b/README.md @@ -437,11 +437,11 @@ Retrieving individual keyed values is simple: ```c Redis *redis = ...; - int status; // Variable in which we'll report error status. + int len; // Variable in which we return the length of the value or an error code // Get the "property" field from the "system:subsystem" hash table - RESP *resp = redisxGetValue(redis, "system:subsystem", "property", &status); - if (status != X_SUCCESS) { + char *value = redisxGetStringValue(redis, "system:subsystem", "property", &len); + if (len < 0) { // Oops something went wrong. ... } @@ -457,13 +457,9 @@ The same goes for top-level keyed values, using `NULL` for the hash table name: ```c // Get value for top-level key (not stored in hash table!) - RESP *resp = redisxGetValue(redis, NULL, "my-key", &status); + char *value = redisxGetStringValue(redis, NULL, "my-key", &len); ``` -The reason the return value is a `RESP` pointer, rather than a string is twofold: (1) because it lets you process possible -error responses from Redis also, and (2) because it lets you deal with unterminated string values, such as binary sequences -of known length. - In turn, setting values is also straightforward: ```c diff --git a/include/redisx.h b/include/redisx.h index 2c6402c..814dac9 100644 --- a/include/redisx.h +++ b/include/redisx.h @@ -253,6 +253,7 @@ RESP *redisxRequest(Redis *redis, const char *command, const char *arg1, const c RESP *redisxArrayRequest(Redis *redis, char *args[], int length[], int n, int *status); int redisxSetValue(Redis *redis, const char *table, const char *key, const char *value, boolean isPipelined); RESP *redisxGetValue(Redis*redis, const char *table, const char *key, int *status); +char *redisxGetStringValue(Redis *redis, const char *table, const char *key, int *len); RedisEntry *redisxGetTable(Redis *redis, const char *table, int *n); RedisEntry *redisxScanTable(Redis *redis, const char *table, const char *pattern, int *n, int *status); int redisxMultiSet(Redis *redis, const char *table, const RedisEntry *entries, int n, boolean isPipelined); diff --git a/src/redisx-tab.c b/src/redisx-tab.c index 0bd8346..da8d01d 100644 --- a/src/redisx-tab.c +++ b/src/redisx-tab.c @@ -197,7 +197,7 @@ int redisxSetValueAsync(RedisClient *cl, const char *table, const char *key, con * Retrieve a variable from Redis, through the interactive connection. This is not the highest throughput mode * (that would be sending asynchronous pipeline request, and then asynchronously collecting the results such as * with redisxSendRequestAsync() / redisxReadReplyAsync()), because it requires separate network roundtrips for each - * and every request. But, it is simple and perfectly good method when one needs to retrieve only a few (<1000) + * and every request. But, it is simple and perfectly good method when one needs to retrieve only a few (<1000) * variables per second... * * The call is effectively implements a Redis GET (if the tale argument is NULL) or HGET call. @@ -210,13 +210,18 @@ int redisxSetValueAsync(RedisClient *cl, const char *table, const char *key, con * * \return A freshly allocated RESP array containing the Redis response, or NULL if no valid * response could be obtained. + * + * \sa redisxGetStringValue() */ RESP *redisxGetValue(Redis *redis, const char *table, const char *key, int *status) { static const char *funcName = "redisxGetValue()"; RESP *reply; - if(redis == NULL) return NULL; + if(redis == NULL) { + if(status) *status = X_NULL; + return NULL; + } if(key == NULL) { *status = redisxError(funcName, X_NAME_INVALID); @@ -231,6 +236,49 @@ RESP *redisxGetValue(Redis *redis, const char *table, const char *key, int *stat return reply; } +/** + * Retrieve a variable from Redis as a string (or byte array), through the interactive connection. This is not the + * highest throughput mode (that would be sending asynchronous pipeline request, and then asynchronously collecting + * the results such as with redisxSendRequestAsync() / redisxReadReplyAsync()), because it requires separate network + * roundtrips for each and every request. But, it is simple and perfectly good method when one needs to retrieve only + * a few (<1000) variables per second... + * + * The call is effectively implements a Redis GET (if the tale argument is NULL) or HGET call. + * + * \param[in] redis Pointer to a Redis instance. + * \param[in] table Hashtable from which to retrieve a value or NULL if to use the global table. + * \param[in] key Field name (i.e. variable name). + * \param[out] len (optional) pointer in which to return the length (>=0) of the value or else + * an error code (<0) defined in xchange.h / redisx.h + * + * \return A freshly allocated RESP array containing the Redis response, or NULL if no valid + * response could be obtained. + * + * \sa redisxGetValue() + */ +char *redisxGetStringValue(Redis *redis, const char *table, const char *key, int *len) { + RESP *reply; + char *str = NULL; + int status; + + if(redis == NULL) { + if(len) *len = X_NULL; + return NULL; + } + + reply = redisxGetValue(redis, table, key, len); + + status = redisxCheckDestroyRESP(reply, RESP_BULK_STRING, 0); + + if(status == X_SUCCESS) { + str = (char *) reply->value; + if(len) *len = reply->n; + } + else if(len) *len = status; + + return str; +} + /** * Sets multiple key/value pairs in a given hash table. *