Skip to content

Commit

Permalink
Simplified redisxScanKeys() and redisxScanTable() API.
Browse files Browse the repository at this point in the history
  • Loading branch information
attipaci committed Feb 17, 2025
1 parent 3585ce9 commit bc8e531
Show file tree
Hide file tree
Showing 7 changed files with 74 additions and 89 deletions.
11 changes: 5 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -925,11 +925,10 @@ something like:
Redis *redis = ...
int nMatches; // We'll return the number of matching Redis keys here...
int status; // We'll return the error status here...
// Return all Redis top-level keywords starting with "system:"
char **keys = redisxScanKeys(redis, "system:*", &nMatches, &status);
if (status != X_SUCCESS) {
char **keys = redisxScanKeys(redis, "system:*", &nMatches);
if (nMatches < 0) {
// Oops something went wrong...
...
}
Expand All @@ -948,8 +947,8 @@ Or, to retrieve the values from a hash table for a set of keywords that match a
...

// Scan all key/value pairs in hash table "system:subsystem"
RedisEntry *entries = redisxScanTable(redis, "system:subsystem", "*", &nMatches, &status);
if (status != X_SUCCESS) {
RedisEntry *entries = redisxScanTable(redis, "system:subsystem", "*", &nMatches);
if (nMatches < 0) {
// Oops something went wrong.
...
}
Expand Down
4 changes: 2 additions & 2 deletions include/redisx.h
Original file line number Diff line number Diff line change
Expand Up @@ -464,10 +464,10 @@ int redisxSetValue(Redis *redis, const char *table, const char *key, const char
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);
RedisEntry *redisxScanTable(Redis *redis, const char *table, const char *pattern, int *n);
int redisxMultiSet(Redis *redis, const char *table, const RedisEntry *entries, int n, boolean confirm);
char **redisxGetKeys(Redis *redis, const char *table, int *n);
char **redisxScanKeys(Redis *redis, const char *pattern, int *n, int *status);
char **redisxScanKeys(Redis *redis, const char *pattern, int *n);
int redisxSetScanCount(Redis *redis, int count);
int redisxGetScanCount(Redis *redis);
void redisxDestroyEntries(RedisEntry *entries, int count);
Expand Down
8 changes: 4 additions & 4 deletions src/redisx-cli.c
Original file line number Diff line number Diff line change
Expand Up @@ -450,18 +450,18 @@ int main(int argc, const char *argv[]) {
prop_error(fn, redisxConnect(redis, FALSE));

if(scan) {
int n = 0, status = X_SUCCESS;
int n = 0;
char **keys;

prop_error(fn, redisxSetScanCount(redis, scanCount));

keys = redisxScanKeys(redis, pattern, &n, &status);
prop_error(fn, status);
keys = redisxScanKeys(redis, pattern, &n);
prop_error(fn, n);

for(i = 0; i < n; i++) printf("\"%s\"\n", keys[i]);
redisxDestroyKeys(keys, n);

if(!cmdargs) return status;
if(!cmdargs) return X_SUCCESS;
}
}

Expand Down
119 changes: 54 additions & 65 deletions src/redisx-tab.c
Original file line number Diff line number Diff line change
Expand Up @@ -588,21 +588,20 @@ static int compare_strings(const void *a, const void *b) {
*
* \param[in] redis Pointer to a Redis instance.
* \param[in] pattern keyword pattern to match, or NULL for all keys.
* \param[out] n Pointer to the integer in which the number of elements
* \param[out] status integer in which to return the status, which is X_SUCCESS (0) if successful, or may
* an error value from redisxRequest(), or:
* \param[out] n Pointer to the integer in which the number of elements (&gt;=0), or else an error
* code (&lt;0), such as:
*
* X_NULL If one of the arguments is NULL
* REDIS_NULL If got a null or empty response from Redis
* UNEXPECTED_RESP If the response from Redis was not the expected array type
*
*lt
* \return An array with pointers to key names from this table or NULL.
*
* @sa redisxGetKeys()
* @sa redisxSetScanCount()
* @sa redisxDestroyKeys()
*/
char **redisxScanKeys(Redis *redis, const char *pattern, int *n, int *status) {
char **redisxScanKeys(Redis *redis, const char *pattern, int *n) {
static const char *fn = "redisxScanKeys";

RESP *reply = NULL;
Expand All @@ -611,20 +610,13 @@ char **redisxScanKeys(Redis *redis, const char *pattern, int *n, int *status) {
char **names = NULL;
char countArg[20];
int capacity = SCAN_INITIAL_STORE_CAPACITY;
int args = 0, i, j;
int args = 0, i, j, status = X_SUCCESS;

if(n == NULL) {
x_error(X_NULL, EINVAL, fn, "parameter 'n' is NULL");
if(status) *status = X_NULL;
return NULL;
}

if(status == NULL) {
x_error(0, EINVAL, fn, "'status' parameter is NULL");
x_error(0, EINVAL, fn, "parameter 'n' is NULL");
return NULL;
}

*status = X_SUCCESS;
*n = 0;

cmd[args++] = "SCAN";
Expand All @@ -649,21 +641,20 @@ char **redisxScanKeys(Redis *redis, const char *pattern, int *n, int *status) {
RESP **components;

if(reply) redisxDestroyRESP(reply);
reply = redisxArrayRequest(redis, cmd, NULL, args, status);

if(*status) break;
reply = redisxArrayRequest(redis, cmd, NULL, args, &status);
if(status < 0) break;

// We expect an array of 2 elements { cursor, { names } }
*status = redisxCheckRESP(reply, RESP_ARRAY, 2);
if(*status) break;
status = redisxCheckRESP(reply, RESP_ARRAY, 2);
if(status < 0) break;

components = (RESP **) reply->value;

*status = redisxCheckRESP(components[0], RESP_BULK_STRING, 0);
if(*status) break;
status = redisxCheckRESP(components[0], RESP_BULK_STRING, 0);
if(status < 0) break;

*status = redisxCheckRESP(components[1], RESP_ARRAY, 0);
if(*status) break;
status = redisxCheckRESP(components[1], RESP_ARRAY, 0);
if(status < 0) break;

// Discard previously received cursor...
free(*pCursor);
Expand Down Expand Up @@ -696,8 +687,8 @@ char **redisxScanKeys(Redis *redis, const char *pattern, int *n, int *status) {

// Store the names we got...
for(i=0; i<count; i++) {
*status = redisxCheckRESP(components[i], RESP_BULK_STRING, 0);
if(*status) break;
status = redisxCheckRESP(components[i], RESP_BULK_STRING, 0);
if(status < 0) break;
names[(*n)++] = (char *) components[i]->value;
components[i]->value = NULL; // de-reference name from RESP.
}
Expand All @@ -709,7 +700,11 @@ char **redisxScanKeys(Redis *redis, const char *pattern, int *n, int *status) {
free(*pCursor);

// Check for errors
if(*status) x_trace(fn, NULL, *status);
if(status) {
if(*n > 0) redisxDestroyKeys(names, *n);
*n = status;
return x_trace_null(fn, NULL);
}

if(!names) return NULL;

Expand Down Expand Up @@ -755,54 +750,45 @@ static int compare_entries(const void *a, const void *b) {
* \param[in] redis Pointer to a Redis instance.
* \param[in] table Name of Redis hash table to scan data from
* \param[in] pattern keyword pattern to match, or NULL for all keys.
* \param[out] n Pointer to the integer in which the number of elements
* \param[out] status integer in which to return the status, which is X_SUCCESS (0) if successful, or may
* an error value from redisxRequest(), or:
* \param[out] n Pointer to the integer in which the number of elements (&gt;=0) or else an
* error code (&lt;0), such as:
*
* X_NULL If one of the arguments is NULL
* REDIS_NULL If got a null or empty response from Redis
* UNEXPECTED_RESP If the response from Redis was not the expected array type
* X_NULL if one of the arguments is NULL
* X_GROUP_INVALID if the table name is empty
* REDIS_NULL if got a null or empty response from Redis
* UNEXPECTED_RESP if the response from Redis was not the expected array type
*
* \return A RedisEntry[] array or NULL.
*
* @sa redisxGetKeys()
* @sa redisxSetScanCount()
* @sa redisxDestroyEntries()
*/
RedisEntry *redisxScanTable(Redis *redis, const char *table, const char *pattern, int *n, int *status) {
RedisEntry *redisxScanTable(Redis *redis, const char *table, const char *pattern, int *n) {
static const char *fn = "redisxScanTable";

RESP *reply = NULL;
RedisEntry *entries = NULL;
const char *cmd[7] = {NULL};
char countArg[20], **pCursor;
int capacity = SCAN_INITIAL_STORE_CAPACITY;
int args= 0, i, j;
int args = 0, i, j, status = X_SUCCESS;

if(n == NULL) {
x_error(X_NULL, EINVAL, fn, "parameter 'n' is NULL");
if(status) *status = X_NULL;
return NULL;
}

if(status == NULL) {
x_error(0, EINVAL, fn, "'status' parameter is NULL");
x_error(0, EINVAL, fn, "parameter 'n' is NULL");
return NULL;
}

if(table == NULL) {
x_error(X_GROUP_INVALID, EINVAL, fn, "'table' parameter is NULL");
*status = X_NULL;
*n = x_error(X_NULL, EINVAL, fn, "'table' parameter is NULL");
return NULL;
}

if(!table[0]) {
x_error(X_GROUP_INVALID, EINVAL, fn, "'table' parameter is empty");
*status = X_NULL;
*n = x_error(X_GROUP_INVALID, EINVAL, fn, "'table' parameter is empty");
return NULL;
}

*status = X_SUCCESS;
*n = 0;

cmd[args++] = "HSCAN";
Expand All @@ -828,20 +814,20 @@ RedisEntry *redisxScanTable(Redis *redis, const char *table, const char *pattern
RESP **components;

if(reply) redisxDestroyRESP(reply);
reply = redisxArrayRequest(redis, cmd, NULL, args, status);
if(*status) break;
reply = redisxArrayRequest(redis, cmd, NULL, args, &status);
if(status < 0) break;

// We expect an array of 2 elements { cursor, { key , value ... } }
*status = redisxCheckRESP(reply, RESP_ARRAY, 2);
if(*status) break;
status = redisxCheckRESP(reply, RESP_ARRAY, 2);
if(status < 0) break;

components = (RESP **) reply->value;

*status = redisxCheckRESP(components[0], RESP_BULK_STRING, 0);
if(*status) break;
status = redisxCheckRESP(components[0], RESP_BULK_STRING, 0);
if(status < 0) break;

*status = redisxCheckRESP(components[1], RESP_ARRAY, 0);
if(*status) break;
status = redisxCheckRESP(components[1], RESP_ARRAY, 0);
if(status < 0) break;

// Discard previously received cursor...
free(*pCursor);
Expand Down Expand Up @@ -877,11 +863,11 @@ RedisEntry *redisxScanTable(Redis *redis, const char *table, const char *pattern
RedisEntry *e = &entries[*n];
int k = i << 1;

*status = redisxCheckRESP(components[k], RESP_BULK_STRING, 0);
if(*status) break;
status = redisxCheckRESP(components[k], RESP_BULK_STRING, 0);
if(status < 0) break;

*status = redisxCheckRESP(components[k+1], RESP_BULK_STRING, 0);
if(*status) break;
status = redisxCheckRESP(components[k+1], RESP_BULK_STRING, 0);
if(status < 0) break;

(*n)++;

Expand All @@ -900,7 +886,11 @@ RedisEntry *redisxScanTable(Redis *redis, const char *table, const char *pattern
free(*pCursor);

// Check for errors
if(*status) x_trace(fn, NULL, *status);
if(status < 0) {
if(*n > 0) redisxDestroyEntries(entries, *n);
*n = status;
return x_trace_null(fn, NULL);
}

if(!entries) return NULL;

Expand Down Expand Up @@ -1000,11 +990,10 @@ int redisxDeleteEntries(Redis *redis, const char *pattern) {
xSplitID(root, &key);

if(redisxIsGlobPattern(root)) {
keys = redisxScanKeys(redis, root, &n, &status);
if(status || !keys) {
keys = redisxScanKeys(redis, root, &n);
if(n < 0) {
free(root);
prop_error(fn, status);
return x_trace(fn, NULL, X_NULL);
prop_error(fn, n);
}
}
else {
Expand All @@ -1028,8 +1017,8 @@ int redisxDeleteEntries(Redis *redis, const char *pattern) {
}

// Otherwise check the table entries...
entries = redisxScanTable(redis, table, key, &nEntries, &status);
if(status == X_SUCCESS) {
entries = redisxScanTable(redis, table, key, &nEntries);
if(nEntries > 0) {
int k;
for(k = 0; k < nEntries; k++) {
const RedisEntry *e = &entries[k];
Expand Down
9 changes: 3 additions & 6 deletions src/redisx.c
Original file line number Diff line number Diff line change
Expand Up @@ -877,7 +877,7 @@ XLookupTable *rConsumeInfoReply(RESP *reply) {
s = xCreateStruct();

// Go line by line...
line = strtok((char *) reply->value, "\n");
line = strtok((char *) reply->value, "\r\n");

// Parse key:value lines into a structure.
while(line) {
Expand All @@ -886,12 +886,11 @@ XLookupTable *rConsumeInfoReply(RESP *reply) {
*sep = '\0';
xSetField(s, xCreateStringField(line, sep + 1));
}
line = strtok(NULL, "\n");
line = strtok(NULL, "\r\n");
}

redisxDestroyRESP(reply);

lookup = xCreateLookup(s, FALSE);
redisxDestroyRESP(reply);
free(s);

return lookup;
Expand Down Expand Up @@ -923,8 +922,6 @@ XLookupTable *redisxGetInfo(Redis *redis, const char *parameter) {
lookup = rConsumeInfoReply(reply);
if(!lookup) return x_trace_null(fn, NULL);

redisxDestroyRESP(reply);

return lookup;
}

Expand Down
2 changes: 1 addition & 1 deletion test/test-info.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ int main() {

role = xLookupField(info, "role");
if(!role) {
fprintf(stderr, "ERROR! role not found (count = %ld)", xLookupCount(info));
fprintf(stderr, "ERROR! role not found (count = %ld)\n", xLookupCount(info));
return 1;
}

Expand Down
10 changes: 5 additions & 5 deletions test/test-tab.c
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,9 @@ int main() {
}
redisxDestroyEntries(e, n1);

e = redisxScanTable(redis, "_test_", "*", &n2, &status);
if(n2 <= 0 || status) {
fprintf(stderr, "ERROR! scan table: n = %d, status = %d\n", n2, status);
e = redisxScanTable(redis, "_test_", "*", &n2);
if(n2 < 0) {
fprintf(stderr, "ERROR! scan table: n = %d\n", n2);
return 1;
}
redisxDestroyEntries(e, n2);
Expand All @@ -103,9 +103,9 @@ int main() {
redisxDestroyKeys(keys, n1);

// Scanned top-level keys
keys = redisxScanKeys(redis, "*", &n2, &status);
keys = redisxScanKeys(redis, "*", &n2);
if(n2 <= 0 || keys == NULL) {
fprintf(stderr, "ERROR! scan keys: n = %d, status = %d\n", n2, status);
fprintf(stderr, "ERROR! scan keys: n = %d\n", n2);
return 1;
}
redisxDestroyKeys(keys, n2);
Expand Down

0 comments on commit bc8e531

Please sign in to comment.