Skip to content

Commit

Permalink
Add new handlersToPrint + expiration filter (redis#49)
Browse files Browse the repository at this point in the history
* Expand API with RDBX_createHandlersToPrint() that support printout customization via
  arguments auxFmt and keyFmt. Can later further refined to printout of valueFmt
  by need, etc. Added also to rdb-cli.
* Expand API with RDBX_createHandlersFilterExpired() to filter based whether a given
  key is expired. Added also to rdb-cli.
  • Loading branch information
moticless authored Jul 18, 2024
1 parent 08757bf commit 5e4f7a2
Show file tree
Hide file tree
Showing 13 changed files with 625 additions and 123 deletions.
12 changes: 9 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -196,17 +196,23 @@ destruction, or when newer block replacing old one.

### rdb-cli usage

Usage: rdb-cli /path/to/dump.rdb [OPTIONS] {json|resp|redis} [FORMAT_OPTIONS]
Usage: rdb-cli /path/to/dump.rdb [OPTIONS] {print|json|resp|redis} [FORMAT_OPTIONS]
OPTIONS:
-l, --log-file <PATH> Path to the log file or stdout (Default: './rdb-cli.log')

Multiple filters combination of keys/types/dbs can be specified:
-k, --key <REGEX> Include only keys that match REGEX
-K --no-key <REGEX> Exclude all keys that match REGEX
-t, --type <TYPE> Include only selected TYPE {str|list|set|zset|hash|module|func}
-T, --no-type <TYPE> Exclude TYPE {str|list|set|zset|hash|module|func}
-d, --dbnum <DBNUM> Include only selected db number
-D, --no-dbnum <DBNUM> Exclude DB number
-e, --expired Include only expired keys
-E, --no-expired Exclude expired keys

FORMAT_OPTIONS ('print'):
-a, --aux-val <FMT> %f=Auxiliary-Field, %v=Auxiliary-Value (Default: "")
-k, --key <FMT> %d=Db %k=Key %v=Value %t=Type %e=Expiry %r=LRU %f=LFU %i=Items
(Default: "%d,%k,%v,%t,%e,%i")
-o, --output <FILE> Specify the output file. If not specified, output to stdout

FORMAT_OPTIONS ('json'):
-i, --include <EXTRAS> To include: {aux-val|func|stream-meta}
Expand Down
1 change: 1 addition & 0 deletions api/librdb-api.h
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ typedef struct RdbKeyInfo {
long long lruIdle; /* -1 if not set */
int lfuFreq; /* -1 if not set */
int opcode;
int dataType; /* See enum RdbDataType */
} RdbKeyInfo;

typedef struct RdbSlotInfo {
Expand Down
26 changes: 26 additions & 0 deletions api/librdb-ext-api.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ typedef struct RdbxReaderFileDesc RdbxReaderFileDesc;
typedef struct RdbxFilter RdbxFilter;
typedef struct RdbxToJson RdbxToJson;
typedef struct RdbxToResp RdbxToResp;
typedef struct RdbxToPrint RdbxToPrint;
typedef struct RdbxRespToRedisLoader RdbxRespToRedisLoader;

/****************************************************************
Expand Down Expand Up @@ -93,6 +94,28 @@ _LIBRDB_API RdbxToJson *RDBX_createHandlersToJson(RdbParser *p,
const char *filename,
RdbxToJsonConf *c);

/****************************************************************
* Create PRINT Handlers
*
* auxFmt - Format string for auxiliary values, where:
* %f = Auxiliary field name
* %v = Auxiliary field value
* keyFmt - Format string for key details, where:
* %d = Database number
* %k = Key
* %v = Value (If the value is a string, it will be printed as escaped string)
* %t = Type
* %e = Expiry
* %r = LRU
* %f = LFU
* %i = Items
*
****************************************************************/
_LIBRDB_API RdbxToPrint *RDBX_createHandlersToPrint(RdbParser *p,
const char *auxFmt,
const char *keyFmt,
const char *outFilename);

/****************************************************************
* Create Filter Handlers
****************************************************************/
Expand All @@ -109,6 +132,9 @@ _LIBRDB_API RdbxFilter *RDBX_createHandlersFilterDbNum(RdbParser *p,
int dbnum,
uint32_t exclude);

_LIBRDB_API RdbxFilter *RDBX_createHandlersFilterExpired(RdbParser *p,
uint32_t exclude);

/****************************************************************
* Create RDB to RESP Handlers
*
Expand Down
100 changes: 78 additions & 22 deletions src/cli/rdb-cli.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,19 @@ typedef struct Options {

void loggerWrap(RdbLogLevel l, const char *msg, ...);

static int getOptArg(int argc, char* argv[], int *at, char *abbrvOpt, char *opt, int *flag, const char **arg) {
/* This function checks if the current argument matches the given option or its
* abbreviation. If a match is found and an argument is expected, it retrieves the
* argument and stores it. It also sets a `flag` or read `extraArg` if provided.
*/
static int getOptArg(int argc, char* argv[], int *at, char *abbrvOpt, char *opt,
int *flag, const char **extraArg) {
if ((strcmp(argv[*at], abbrvOpt) == 0) || (strcmp(argv[*at], opt) == 0)) {
if (arg) {
if (extraArg) {
if ((*at) + 1 == argc) {
fprintf(stderr, "%s (%s) requires one argument.", opt, abbrvOpt);
exit(RDB_ERR_GENERAL);
exit(1);
}
*arg = argv[++(*at)];
*extraArg = argv[++(*at)];
}
if (flag) *flag = 1;
return 1;
Expand All @@ -38,16 +43,23 @@ static int getOptArg(int argc, char* argv[], int *at, char *abbrvOpt, char *opt,
}
}

static int getOptArgVal(int argc, char* argv[], int *at, char *abbrvOpt, char *opt, int *flag, int *val, int min, int max) {
/* This function checks if the current argument matches the given option or its
* abbreviation. If a match is found, it retrieves the argument, converts it to
* an integer, and verifies that it is within the specified boundaries. It also
* sets a `flag` if provided.
*/
static int getOptArgVal(int argc, char* argv[], int *at, char *abbrvOpt, char *opt,
int *flag, int *val, int min, int max) {
const char *valStr;
if (getOptArg(argc, argv, at, abbrvOpt, opt, flag, &valStr)) {

*val = atoi(valStr);

/* check boundaries. Condition support also the limits INT_MAX and INT_MIN. */
if (!((*val>=min) && (*val <=max))) {
loggerWrap(RDB_LOG_ERR, "Value of %s (%s) must be a integer between %d and %d", opt, abbrvOpt, min, max);
exit(RDB_ERR_GENERAL);
loggerWrap(RDB_LOG_ERR, "Value of %s (%s) must be a integer between %d and %d",
opt, abbrvOpt, min, max);
exit(1);
}
return 1;
}
Expand Down Expand Up @@ -85,16 +97,23 @@ static void printUsage(int shortUsage) {
return;
}
printf("[v%s] ", RDB_getLibVersion(NULL,NULL,NULL));
printf("Usage: rdb-cli /path/to/dump.rdb [OPTIONS] {json|resp|redis} [FORMAT_OPTIONS]\n");
printf("Usage: rdb-cli /path/to/dump.rdb [OPTIONS] {print|json|resp|redis} [FORMAT_OPTIONS]\n");
printf("OPTIONS:\n");
printf("\t-l, --log-file <PATH> Path to the log file or stdout (Default: './rdb-cli.log')\n\n");
printf("\tMultiple filters combination of keys/types/dbs can be specified:\n");
printf("\t-l, --log-file <PATH> Path to the log file or stdout (Default: './rdb-cli.log')\n");
printf("\t-k, --key <REGEX> Include only keys that match REGEX\n");
printf("\t-K --no-key <REGEX> Exclude all keys that match REGEX\n");
printf("\t-t, --type <TYPE> Include only selected TYPE {str|list|set|zset|hash|module|func}\n");
printf("\t-T, --no-type <TYPE> Exclude TYPE {str|list|set|zset|hash|module|func}\n");
printf("\t-d, --dbnum <DBNUM> Include only selected db number\n");
printf("\t-D, --no-dbnum <DBNUM> Exclude DB number\n\n");
printf("\t-D, --no-dbnum <DBNUM> Exclude DB number\n");
printf("\t-e, --expired Include only expired keys\n");
printf("\t-E, --no-expired Exclude expired keys\n\n");

printf("FORMAT_OPTIONS ('print'):\n");
printf("\t-a, --aux-val <FMT> %%f=Auxiliary-Field, %%v=Auxiliary-Value (Default: \"\") \n");
printf("\t-k, --key <FMT> %%d=Db %%k=Key %%v=Value %%t=Type %%e=Expiry %%r=LRU %%f=LFU %%i=Items\n");
printf("\t (Default: \"%%d,%%k,%%v,%%t,%%e,%%i\")\n");
printf("\t-o, --output <FILE> Specify the output file. If not specified, output to stdout\n\n");

printf("FORMAT_OPTIONS ('json'):\n");
printf("\t-i, --include <EXTRAS> To include: {aux-val|func|stream-meta|db-info}\n");
Expand Down Expand Up @@ -130,7 +149,8 @@ static RdbRes formatJson(RdbParser *parser, int argc, char **argv) {
extern const char *jsonMetaPrefix;
const char *includeArg;
const char *output = NULL;/*default:stdout*/
int includeDbInfo=0, includeStreamMeta=0, includeFunc=0, includeAuxField=0, flatten=0; /*without*/
int includeDbInfo=0, includeStreamMeta=0, includeFunc=0, includeAuxField=0,
flatten=0;

/* parse specific command options */
for (int at = 1; at < argc; ++at) {
Expand Down Expand Up @@ -169,6 +189,27 @@ static RdbRes formatJson(RdbParser *parser, int argc, char **argv) {
return RDB_OK;
}

static RdbRes formatPrint(RdbParser *parser, int argc, char **argv) {
const char *auxFmt = NULL, *keyFmt = "%d,%k,%v,%t,%e,%i";
const char *output = NULL;/*default:stdout*/

/* parse specific command options */
for (int at = 1; at < argc; ++at) {
char *opt = argv[at];
if (getOptArg(argc, argv, &at, "-o", "--output", NULL, &output)) continue;
if (getOptArg(argc, argv, &at, "-a", "--aux-val", NULL, &auxFmt)) continue;
if (getOptArg(argc, argv, &at, "-k", "--key", NULL, &keyFmt)) continue;
loggerWrap(RDB_LOG_ERR, "Invalid 'print' [FORMAT_OPTIONS] argument: %s\n", opt);
printUsage(1);
return RDB_ERR_GENERAL;
}

if (RDBX_createHandlersToPrint(parser, auxFmt, keyFmt, output) == NULL)
return RDB_ERR_GENERAL;

return RDB_OK;
}

static RdbRes formatRedis(RdbParser *parser, int argc, char **argv) {
const char *output = NULL;
RdbxRedisAuth auth = {0};
Expand Down Expand Up @@ -297,9 +338,10 @@ int matchRdbDataType(const char *dataTypeStr) {
if (!strcmp(dataTypeStr, "stream")) return RDB_DATA_TYPE_STREAM;
if (!strcmp(dataTypeStr, "func")) return RDB_DATA_TYPE_FUNCTION;

loggerWrap(RDB_LOG_ERR, "Invalid TYPE argument (%s). Valid values: str, list, set, zset, hash, module, stream, func",
loggerWrap(RDB_LOG_ERR,
"Invalid TYPE argument (%s). Valid values: str, list, set, zset, hash, module, stream, func",
dataTypeStr);
exit(RDB_ERR_GENERAL);
exit(1);
}

int readCommonOptions(RdbParser *p, int argc, char* argv[], Options *options, int applyFilters) {
Expand All @@ -320,47 +362,60 @@ int readCommonOptions(RdbParser *p, int argc, char* argv[], Options *options, in

if (getOptArg(argc, argv, &at, "-k", "--key", NULL, &keyFilter)) {
if (applyFilters && (!RDBX_createHandlersFilterKey(p, keyFilter, 0)))
exit(RDBX_ERR_FAILED_CREATE_FILTER);
exit(1);
continue;
}

if (getOptArg(argc, argv, &at, "-K", "--no-key", NULL, &keyFilter)) {
if (applyFilters && (!RDBX_createHandlersFilterKey(p, keyFilter, 1)))
exit(RDBX_ERR_FAILED_CREATE_FILTER);
exit(1);
continue;
}

if (getOptArg(argc, argv, &at, "-t", "--type", NULL, &typeFilter)) {
if ((applyFilters) && (!RDBX_createHandlersFilterType(p, matchRdbDataType(typeFilter), 0)))
exit(RDBX_ERR_FAILED_CREATE_FILTER);
exit(1);
continue;
}

if (getOptArg(argc, argv, &at, "-T", "--no-type", NULL, &typeFilter)) {
if ((applyFilters) && (!RDBX_createHandlersFilterType(p, matchRdbDataType(typeFilter), 1)))
exit(RDBX_ERR_FAILED_CREATE_FILTER);
exit(1);
continue;
}

if (getOptArgVal(argc, argv, &at, "-d", "--dbnum", NULL, &dbNumFilter, 0, INT_MAX)) {
if ((applyFilters) && (!RDBX_createHandlersFilterDbNum(p, dbNumFilter, 0)))
exit(RDBX_ERR_FAILED_CREATE_FILTER);
exit(1);
continue;
}

if (getOptArgVal(argc, argv, &at, "-D", "--no-dbnum", NULL, &dbNumFilter, 0, INT_MAX)) {
if ((applyFilters) && (!RDBX_createHandlersFilterDbNum(p, dbNumFilter, 1)))
exit(RDBX_ERR_FAILED_CREATE_FILTER);
exit(1);
continue;
}

if (getOptArg(argc, argv, &at, "-e", "--expired", NULL, NULL)) {
if ((applyFilters) && (!RDBX_createHandlersFilterExpired(p, 0)))
exit(1);
continue;
}

if (getOptArg(argc, argv, &at, "-E", "--no-expired", NULL, NULL)) {
if ((applyFilters) && (!RDBX_createHandlersFilterExpired(p, 1)))
exit(1);
continue;
}

if (strcmp(opt, "json") == 0) { options->formatFunc = formatJson; break; }
else if (strcmp(opt, "resp") == 0) { options->formatFunc = formatResp; break; }
else if (strcmp(opt, "redis") == 0) { options->formatFunc = formatRedis; break; }
else if (strcmp(opt, "print") == 0) { options->formatFunc = formatPrint; break; }

loggerWrap(RDB_LOG_ERR, "At argv[%d], unexpected OPTIONS argument: %s\n", at, opt);
printUsage(1);
exit(RDB_ERR_GENERAL);
exit(1);
}
return at;
}
Expand Down Expand Up @@ -397,7 +452,8 @@ int main(int argc, char **argv)
}

if ((logfile = fopen(options.logfilePath, "w")) == NULL) {
printf("Error opening log file for writing `%s`: %s\n", options.logfilePath, strerror(errno));
printf("Error opening log file for writing `%s`: %s\n",
options.logfilePath, strerror(errno));
return RDB_ERR_GENERAL;
}

Expand Down
Loading

0 comments on commit 5e4f7a2

Please sign in to comment.