예제 #1
0
TEST simple_concurrency() {
	delete_file();
	rliteContext *context1 = rliteConnect(FILEPATH, 0);
	rliteContext *context2 = rliteConnect(FILEPATH, 0);

	rliteReply* reply;
	size_t argvlen[100];

	{
		char* argv[100] = {"GET", "key", NULL};
		reply = rliteCommandArgv(context1, populateArgvlen(argv, argvlen), argv, argvlen);
		EXPECT_REPLY_NIL(reply);
		rliteFreeReplyObject(reply);
	}

	{
		char* argv[100] = {"set", "key", "value", NULL};
		reply = rliteCommandArgv(context2, populateArgvlen(argv, argvlen), argv, argvlen);
		EXPECT_REPLY_STATUS(reply, "OK", 2);
		rliteFreeReplyObject(reply);
	}
	{
		char* argv[100] = {"GET", "key", NULL};
		reply = rliteCommandArgv(context1, populateArgvlen(argv, argvlen), argv, argvlen);
		EXPECT_REPLY_STR(reply, "value", 5);
		rliteFreeReplyObject(reply);
	}

	rliteFree(context1);
	rliteFree(context2);
	unlink(FILEPATH);
	PASS();
}
예제 #2
0
파일: set-test.c 프로젝트: jplevyak/rlite
TEST test_sdiffstore() {
	rliteContext *context = rliteConnect(":memory:", 0);
	size_t argvlen[100];

	char *m1 = "mymember", *m2 = "member2", *s1 = "myset", *s2 = "myset2", *t = "target";
	sadd(context, s1, m1);
	sadd(context, s1, m2);
	sadd(context, s2, m1);
	sadd(context, s2, "meh");

	rliteReply* reply;
	{
		char* argv[100] = {"sdiffstore", t, s1, s2, NULL};
		reply = rliteCommandArgv(context, populateArgvlen(argv, argvlen), argv, argvlen);
		EXPECT_REPLY_INTEGER(reply, 1);
		rliteFreeReplyObject(reply);
	}

	{
		char* argv[100] = {"smembers", t, NULL};

		reply = rliteCommandArgv(context, populateArgvlen(argv, argvlen), argv, argvlen);
		EXPECT_REPLY_LEN(reply, 1);
		EXPECT_REPLY_STR(reply->element[0], m2, strlen(m2));
		rliteFreeReplyObject(reply);
	}

	rliteFree(context);
	return 0;
}
예제 #3
0
파일: set-test.c 프로젝트: jplevyak/rlite
TEST test_scard() {
	rliteContext *context = rliteConnect(":memory:", 0);
	size_t argvlen[100];

	if (randomSadd(context, "myset", 5) != 0) {
		return 1;
	}

	rliteReply* reply;
	char* argv[100] = {"scard", "myset", NULL};
	reply = rliteCommandArgv(context, populateArgvlen(argv, argvlen), argv, argvlen);
	EXPECT_REPLY_INTEGER(reply, 5);
	rliteFreeReplyObject(reply);

	if (randomSadd(context, "myset", 3) != 0) {
		return 1;
	}

	char* argv2[100] = {"scard", "myset", NULL};
	reply = rliteCommandArgv(context, populateArgvlen(argv2, argvlen), argv2, argvlen);
	EXPECT_REPLY_INTEGER(reply, 8);
	rliteFreeReplyObject(reply);

	rliteFree(context);
	return 0;
}
예제 #4
0
파일: hsort-test.c 프로젝트: jplevyak/rlite
TEST test_sort_alpha() {
	rliteContext *context = rliteConnect(":memory:", 0);

	rliteReply* reply;
	size_t argvlen[100];

	{
		char* argv[100] = {"sadd", "key", "31", "12", "41", NULL};
		reply = rliteCommandArgv(context, populateArgvlen(argv, argvlen), argv, argvlen);
		EXPECT_REPLY_INTEGER(reply, 3);
		rliteFreeReplyObject(reply);
	}

	{
		char* argv[100] = {"sort", "key", NULL};
		reply = rliteCommandArgv(context, populateArgvlen(argv, argvlen), argv, argvlen);
		EXPECT_REPLY_LEN(reply, 3);
		EXPECT_REPLY_STR(reply->element[0], "12", 2);
		EXPECT_REPLY_STR(reply->element[1], "31", 2);
		EXPECT_REPLY_STR(reply->element[2], "41", 2);
		rliteFreeReplyObject(reply);
	}

	rliteFree(context);
	PASS();
}
예제 #5
0
파일: hsort-test.c 프로젝트: jplevyak/rlite
TEST test_sort_get_pattern_get_pound() {
	rliteContext *context = rliteConnect(":memory:", 0);

	rliteReply* reply;
	size_t argvlen[100];

	{
		char* argv[100] = {"rpush", "key", "3", "1", "2", NULL};
		reply = rliteCommandArgv(context, populateArgvlen(argv, argvlen), argv, argvlen);
		EXPECT_REPLY_INTEGER(reply, 3);
		rliteFreeReplyObject(reply);
	}

	{
		char* argv[100] = {"sort", "key", "GET", "#", NULL};
		reply = rliteCommandArgv(context, populateArgvlen(argv, argvlen), argv, argvlen);
		EXPECT_REPLY_LEN(reply, 3);
		EXPECT_REPLY_STR(reply->element[0], "1", 1);
		EXPECT_REPLY_STR(reply->element[1], "2", 1);
		EXPECT_REPLY_STR(reply->element[2], "3", 1);
		rliteFreeReplyObject(reply);
	}

	rliteFree(context);
	PASS();
}
예제 #6
0
파일: set-test.c 프로젝트: jplevyak/rlite
TEST test_srem() {
	rliteContext *context = rliteConnect(":memory:", 0);
	size_t argvlen[100];

	char *m1 = "mymember", *m2 = "member2";
	sadd(context, "myset", m1);
	sadd(context, "myset", m2);

	rliteReply* reply;
	{
		char* argv[100] = {"srem", "myset", m1, "other", m2, NULL};

		reply = rliteCommandArgv(context, populateArgvlen(argv, argvlen), argv, argvlen);
		EXPECT_REPLY_INTEGER(reply, 2);
		rliteFreeReplyObject(reply);
	}

	{
		char* argv[100] = {"exists", "mykey", NULL};

		reply = rliteCommandArgv(context, populateArgvlen(argv, argvlen), argv, argvlen);
		EXPECT_REPLY_INTEGER(reply, 0);
		rliteFreeReplyObject(reply);
	}

	rliteFree(context);
	return 0;
}
예제 #7
0
TEST threads_concurrency() {
	delete_file();
	rliteContext *context = rliteConnect(FILEPATH, 0);

	pthread_t thread;
	pthread_create(&thread, NULL, increment, NULL);

	rliteReply* reply;
	size_t argvlen[100];
	char* argv[100] = {"GET", "key", NULL};
	int argc = populateArgvlen(argv, argvlen);
	long long val;
	char tmp[40];

	do {
		reply = rliteCommandArgv(context, argc, argv, argvlen);
		if (reply->type == RLITE_REPLY_NIL) {
			val = 0;
		} else  if (reply->type != RLITE_REPLY_STRING) {
			fprintf(stderr, "Expected incremented value to be a string, got %d instead on line %d\n", reply->type, __LINE__);
			rliteFreeReplyObject(reply);
			break;
		} else {
			memcpy(tmp, reply->str, reply->len);
			tmp[reply->len] = 0;
			val = strtoll(tmp, NULL, 10);
		}
		rliteFreeReplyObject(reply);
	} while (val < INCREMENT_LIMIT);

	rliteFree(context);
	pthread_join(thread, NULL);
	PASS();
}
예제 #8
0
파일: echo-test.c 프로젝트: jplevyak/rlite
TEST test_echo_oom() {
	size_t argvlen[2];
	char* argv[2];
	rliteReply* reply;
	argvlen[0] = 4;
	argv[0] = "echo";
	argvlen[1] = 11;
	argv[1] = "hello world";
	rliteContext *context = rliteConnect(":memory:", 0);
	int i;
	for (i = 1;;i++) {
		test_mode = 1;
		test_mode_counter = i;
		reply = rliteCommandArgv(context, 2, argv, argvlen);
		if (reply != NULL) {
			if (i == 1) {
				fprintf(stderr, "No OOM triggered\n");
				test_mode = 0;
				FAIL();
			}
            EXPECT_REPLY_STR(reply, argv[1], argvlen[1]);
			break;
		}
	}

	test_mode = 0;
	rliteFreeReplyObject(reply);
	rliteFree(context);
	PASS();
}
예제 #9
0
파일: echo-test.c 프로젝트: jplevyak/rlite
TEST test_ping_oom() {
	size_t argvlen[2];
	char* argv[2];
	rliteReply* reply;
	argvlen[0] = 4;
	argv[0] = "PING";
	rliteContext *context = rliteConnect(":memory:", 0);
	int i;
	for (i = 1;;i++) {
		test_mode = 1;
		test_mode_counter = i;
		reply = rliteCommandArgv(context, 1, argv, argvlen);
		if (reply != NULL) {
			if (i == 1) {
				fprintf(stderr, "No OOM triggered\n");
				test_mode = 0;
				FAIL();
			}
			EXPECT_REPLY_STATUS(reply, "PONG", 4);
			break;
		}
	}

	test_mode = 0;
	rliteFreeReplyObject(reply);
	rliteFree(context);
	PASS();
}
예제 #10
0
파일: echo-test.c 프로젝트: jplevyak/rlite
TEST test_echo_wrong_arity_oom() {
	size_t argvlen[2];
	char* argv[2];
	rliteReply* reply;
	argvlen[0] = 4;
	argv[0] = "echo";
	rliteContext *context = rliteConnect(":memory:", 0);
	int i;
	for (i = 1;;i++) {
		test_mode = 1;
		test_mode_counter = i;
		reply = rliteCommandArgv(context, 1, argv, argvlen);
		if (reply != NULL) {
			if (i == 1) {
				fprintf(stderr, "No OOM triggered\n");
				test_mode = 0;
				FAIL();
			}
			EXPECT_REPLY_ERROR(reply);
			const char *err = "wrong number of arguments for 'echo' command";
			ASSERT_EQ(memcmp(reply->str, err, strlen(err)), 0);
			break;
		}
	}

	test_mode = 0;
	rliteFreeReplyObject(reply);
	rliteFree(context);
	PASS();
}
예제 #11
0
파일: set-test.c 프로젝트: jplevyak/rlite
static void sadd(rliteContext* context, char *key, char *element) {
	rliteReply* reply;
	size_t argvlen[100];
	char* argv[100] = {"sadd", key, element, NULL};
	reply = rliteCommandArgv(context, populateArgvlen(argv, argvlen), argv, argvlen);
	rliteFreeReplyObject(reply);
}
예제 #12
0
파일: set-test.c 프로젝트: jplevyak/rlite
TEST test_srandmember_10_non_unique() {
	rliteContext *context = rliteConnect(":memory:", 0);
	size_t argvlen[100];

	long i;
	char *m1 = "mymember", *m2 = "member2";
	sadd(context, "myset", m1);
	sadd(context, "myset", m2);

	rliteReply* reply;
	{
		char* argv[100] = {"srandmember", "myset", "-10", NULL};

		reply = rliteCommandArgv(context, populateArgvlen(argv, argvlen), argv, argvlen);
		EXPECT_REPLY_LEN(reply, 10);

		for (i = 0; i < 10; i++) {
			if (!(reply->element[i]->len == (int)strlen(m1) && memcmp(reply->element[i]->str, m1, reply->element[i]->len) == 0) &&
				!(reply->element[i]->len == (int)strlen(m2) && memcmp(reply->element[i]->str, m2, reply->element[i]->len) == 0)) {
				FAIL();
			}
		}

		rliteFreeReplyObject(reply);
	}
	rliteFree(context);
	return 0;
}
예제 #13
0
파일: set-test.c 프로젝트: jplevyak/rlite
static int randomSadd(rliteContext* context, char *key, int elements) {
	rliteReply* reply;
	if (elements == 0) {
		elements = 3;
	}
	char* argv[100] = {"sadd", key, NULL};
	int i, j, len;
	for (i = 0; i < elements; i++) {
		len = 5 + floor(((float)rand() / RAND_MAX) * 10);
		argv[2 + i] = malloc(sizeof(char) * len);
		for (j = 0; j < len - 1; j++) {
			argv[2 + i][j] = 'a' + (int)floor(((float)rand() / RAND_MAX) * 25);
		}
		argv[2 + i][len - 1] = 0;
	}

	size_t argvlen[100];
	reply = rliteCommandArgv(context, populateArgvlen(argv, argvlen), argv, argvlen);
	EXPECT_REPLY_INTEGER(reply, elements);
	rliteFreeReplyObject(reply);
	for (i = 0; i < elements; i++) {
		free(argv[2 + i]);
	}
	return 0;
}
예제 #14
0
파일: echo-test.c 프로젝트: jplevyak/rlite
TEST test_not_null_terminated_long() {
	size_t argvlen[4];
	char* argv[4];
	rliteReply* reply;
	argvlen[0] = 6;
	argv[0] = "lrange";
	argvlen[1] = 3;
	argv[1] = "key";
	argvlen[2] = 1;
	argv[2] = "0";
	argvlen[3] = 2;
	argv[3] = malloc(sizeof(char) * 4);
	argv[3][0] = '-';
	argv[3][1] = '1';
	argv[3][2] = 'z';

	rliteContext *context = rliteConnect(":memory:", 0);
	reply = rliteCommandArgv(context, 4, argv, argvlen);
	ASSERT_EQ(reply->type, RLITE_REPLY_ARRAY);

	rliteFreeReplyObject(reply);
	rliteFree(context);
	free(argv[3]);
	PASS();
}
예제 #15
0
파일: hsort-test.c 프로젝트: jplevyak/rlite
TEST test_sortby_hash() {
	rliteContext *context = rliteConnect(":memory:", 0);

	rliteReply* reply;
	size_t argvlen[100];

	{
		char* argv[100] = {"hset", "wobj_a", "weight", "1", NULL};
		reply = rliteCommandArgv(context, populateArgvlen(argv, argvlen), argv, argvlen);
		EXPECT_REPLY_INTEGER(reply, 1);
		rliteFreeReplyObject(reply);
	}

	{
		char* argv[100] = {"hset", "wobj_b", "weight", "2", NULL};
		reply = rliteCommandArgv(context, populateArgvlen(argv, argvlen), argv, argvlen);
		EXPECT_REPLY_INTEGER(reply, 1);
		rliteFreeReplyObject(reply);
	}

	{
		char* argv[100] = {"hset", "wobj_c", "weight", "3", NULL};
		reply = rliteCommandArgv(context, populateArgvlen(argv, argvlen), argv, argvlen);
		EXPECT_REPLY_INTEGER(reply, 1);
		rliteFreeReplyObject(reply);
	}

	{
		char* argv[100] = {"rpush", "key", "b", "a", "c", NULL};
		reply = rliteCommandArgv(context, populateArgvlen(argv, argvlen), argv, argvlen);
		EXPECT_REPLY_INTEGER(reply, 3);
		rliteFreeReplyObject(reply);
	}

	{
		char* argv[100] = {"sort", "key", "by", "wobj_*->weight", NULL};
		reply = rliteCommandArgv(context, populateArgvlen(argv, argvlen), argv, argvlen);
		EXPECT_REPLY_LEN(reply, 3);
		EXPECT_REPLY_STR(reply->element[0], "a", 1);
		EXPECT_REPLY_STR(reply->element[1], "b", 1);
		EXPECT_REPLY_STR(reply->element[2], "c", 1);
		rliteFreeReplyObject(reply);
	}

	rliteFree(context);
	PASS();
}
예제 #16
0
파일: set-test.c 프로젝트: jplevyak/rlite
TEST test_smove() {
	rliteContext *context = rliteConnect(":memory:", 0);
	size_t argvlen[100];

	sadd(context, "myset", "mymember");

	rliteReply* reply;
	{
		char* argv[100] = {"smove", "myset", "otherset", "mymember", NULL};
		reply = rliteCommandArgv(context, populateArgvlen(argv, argvlen), argv, argvlen);
		EXPECT_REPLY_INTEGER(reply, 1);
		rliteFreeReplyObject(reply);
	}

	{
		char* argv[100] = {"sismember", "myset", "mymember", NULL};
		reply = rliteCommandArgv(context, populateArgvlen(argv, argvlen), argv, argvlen);
		EXPECT_REPLY_INTEGER(reply, 0);
		rliteFreeReplyObject(reply);
	}

	{
		char* argv[100] = {"sismember", "otherset", "mymember", NULL};
		reply = rliteCommandArgv(context, populateArgvlen(argv, argvlen), argv, argvlen);
		EXPECT_REPLY_INTEGER(reply, 1);
		rliteFreeReplyObject(reply);
	}

	{
		char* argv[100] = {"smove", "otherset", "myset", "member2", NULL};
		reply = rliteCommandArgv(context, populateArgvlen(argv, argvlen), argv, argvlen);
		EXPECT_REPLY_INTEGER(reply, 0);
		rliteFreeReplyObject(reply);
	}

	{
		char* argv[100] = {"sismember", "myset", "member2", NULL};
		reply = rliteCommandArgv(context, populateArgvlen(argv, argvlen), argv, argvlen);
		EXPECT_REPLY_INTEGER(reply, 0);
		rliteFreeReplyObject(reply);
	}

	rliteFree(context);
	return 0;
}
예제 #17
0
파일: set-test.c 프로젝트: jplevyak/rlite
TEST test_spop() {
	rliteContext *context = rliteConnect(":memory:", 0);
	size_t argvlen[100];

	char *m1 = "mymember", *m2 = "member2";
	sadd(context, "myset", m1);
	sadd(context, "myset", m2);

	rliteReply* reply;
	{
		char* argv[100] = {"spop", "myset", NULL};
		reply = rliteCommandArgv(context, populateArgvlen(argv, argvlen), argv, argvlen);
		ASSERT_EQ(reply->type, RLITE_REPLY_STRING);
		if (!(reply->len == (int)strlen(m1) && memcmp(reply->str, m1, reply->len) == 0) &&
			!(reply->len == (int)strlen(m2) && memcmp(reply->str, m2, reply->len) == 0)) {
			FAIL();
		}
		rliteFreeReplyObject(reply);
	}

	{
		char* argv[100] = {"spop", "myset", NULL};
		reply = rliteCommandArgv(context, populateArgvlen(argv, argvlen), argv, argvlen);
		ASSERT_EQ(reply->type, RLITE_REPLY_STRING);
		if (!(reply->len == (int)strlen(m1) && memcmp(reply->str, m1, reply->len) == 0) &&
			!(reply->len == (int)strlen(m2) && memcmp(reply->str, m2, reply->len) == 0)) {
			FAIL();
		}
		rliteFreeReplyObject(reply);
	}

	{
		char* argv[100] = {"spop", "myset", NULL};
		reply = rliteCommandArgv(context, populateArgvlen(argv, argvlen), argv, argvlen);
		EXPECT_REPLY_NIL(reply);
		rliteFreeReplyObject(reply);
	}

	rliteFree(context);
	return 0;
}
예제 #18
0
파일: set-test.c 프로젝트: jplevyak/rlite
TEST test_sadd() {
	rliteContext *context = rliteConnect(":memory:", 0);

	rliteReply* reply;
	char* argv[100] = {"sadd", "myset", "member1", "member2", "anothermember", "member1", NULL};
	size_t argvlen[100];
	reply = rliteCommandArgv(context, populateArgvlen(argv, argvlen), argv, argvlen);
	EXPECT_REPLY_INTEGER(reply, 3);
	rliteFreeReplyObject(reply);

	rliteFree(context);
	return 0;
}
예제 #19
0
파일: echo-test.c 프로젝트: jplevyak/rlite
TEST test_ping() {
	size_t argvlen[2];
	char* argv[2];
	rliteReply* reply;
	argvlen[0] = 4;
	argv[0] = "PING";
	rliteContext *context = rliteConnect(":memory:", 0);
	reply = rliteCommandArgv(context, 1, argv, argvlen);
	EXPECT_REPLY_STATUS(reply, "PONG", 4);

	rliteFreeReplyObject(reply);
	rliteFree(context);
	PASS();
}
예제 #20
0
파일: rlite.c 프로젝트: miedzinski/rlite-py
static PyObject *Rlite_command(hirlite_RliteObject *self, PyObject *args) {
    PyObject *object;
    int i, argc;
    char **argv;
    size_t *argvlen;
    PyObject *bytes;
    char *str;
    size_t len;
    rliteReply *reply;

    argc = (int)PyTuple_Size(args);
    argv = malloc(sizeof(char *) * argc);
    if (!argv)
        return NULL;
    argvlen = malloc(sizeof(size_t) * argc);
    if (!argvlen) {
        free(argv);
        return NULL;
    }

    for (i = 0; i < argc; i++) {
        object = PyTuple_GetItem(args, i);
        if (PyUnicode_Check(object))
            bytes = PyUnicode_AsASCIIString(object);
        else
            bytes = PyObject_Bytes(object);

        if (bytes == NULL)
            return NULL;

        argvlen[i] = len = PyBytes_Size(bytes);
        str = PyBytes_AsString(bytes);
        argv[i] = (char*)malloc(len+1);
        memcpy(argv[i], str, len);
        argv[i][len] = '\0';
        Py_DECREF(bytes);
    }

    reply = rliteCommandArgv(self->context, argc, argv, argvlen);
    object = replyToPyObject(self, reply);

    for (i = 0; i < argc; i++) {
        free(argv[i]);
    }
    free(argv);
    free(argvlen);
    rliteFreeReplyObject(reply);

    return object;
}
예제 #21
0
파일: echo-test.c 프로젝트: jplevyak/rlite
TEST test_echo_wrong_arity() {
	size_t argvlen[2];
	char* argv[2];
	rliteReply* reply;
	argvlen[0] = 4;
	argv[0] = "echo";
	rliteContext *context = rliteConnect(":memory:", 0);
	reply = rliteCommandArgv(context, 1, argv, argvlen);
	EXPECT_REPLY_ERROR(reply);
	const char *err = "wrong number of arguments for 'echo' command";
	ASSERT_EQ(memcmp(reply->str, err, strlen(err)), 0);

	rliteFreeReplyObject(reply);
	rliteFree(context);
	PASS();
}
예제 #22
0
파일: echo-test.c 프로젝트: jplevyak/rlite
TEST test_echo() {
	size_t argvlen[2];
	char* argv[2];
	rliteReply* reply;
	argvlen[0] = 4;
	argv[0] = "echo";
	argvlen[1] = 11;
	argv[1] = "hello world";
	rliteContext *context = rliteConnect(":memory:", 0);
	reply = rliteCommandArgv(context, 2, argv, argvlen);
	EXPECT_REPLY_STR(reply, argv[1], argvlen[1]);

	rliteFreeReplyObject(reply);
	rliteFree(context);
	PASS();
}
예제 #23
0
static void *increment(void *UNUSED(arg)) {
	rliteContext *context = rliteConnect(FILEPATH, 0);
	rliteReply* reply;
	size_t argvlen[100];
	char* argv[100] = {"INCR", "key", NULL};
	int argc = populateArgvlen(argv, argvlen);
	long long val = 0;
	do {
		reply = rliteCommandArgv(context, argc, argv, argvlen);
		if (reply->type != RLITE_REPLY_INTEGER || reply->integer < val) {
			fprintf(stderr, "Expected incremented value to be an integer greater than %lld, got %d (%lld) instead\n", val, reply->type, reply->integer);
			val = INCREMENT_LIMIT; // break after free
		} else {
			val = reply->integer;
		}
		rliteFreeReplyObject(reply);
	} while (val < INCREMENT_LIMIT);
	rliteFree(context);
	return NULL;
}
예제 #24
0
파일: set-test.c 프로젝트: jplevyak/rlite
TEST test_sunion() {
	rliteContext *context = rliteConnect(":memory:", 0);
	size_t argvlen[100];

	char *m1 = "mymember", *m2 = "member2", *s1 = "myset", *s2 = "myset2";
	sadd(context, s1, m1);
	sadd(context, s1, m2);
	sadd(context, s2, m1);
	sadd(context, s2, "meh");

	rliteReply* reply;
	{
		char* argv[100] = {"sunion", s1, s2, NULL};
		reply = rliteCommandArgv(context, populateArgvlen(argv, argvlen), argv, argvlen);
		EXPECT_REPLY_LEN(reply, 3);
		rliteFreeReplyObject(reply);
	}

	rliteFree(context);
	return 0;
}
예제 #25
0
파일: scripting.c 프로젝트: jqk6/rlite
int luaRedisGenericCommand(lua_State *lua, int raise_error) {
	int j, argc = lua_gettop(lua);
	struct rliteCommand *cmd;
	rliteClient *c = lua_client;
	rliteReply *reply;

	/* Cached across calls. */
	static char **argv = NULL;
	static size_t *argvlen = NULL;
	static int argv_size = 0;
	static int inuse = 0;   /* Recursive calls detection. */

	/* By using Lua debug hooks it is possible to trigger a recursive call
	 * to luaRedisGenericCommand(), which normally should never happen.
	 * To make this function reentrant is futile and makes it slower, but
	 * we should at least detect such a misuse, and abort. */
	if (inuse) {
		char *recursion_warning =
			"luaRedisGenericCommand() recursive call detected. "
			"Are you doing funny stuff with Lua debug hooks?";
		rliteLog(RLITE_WARNING,"%s",recursion_warning);
		luaPushError(lua,recursion_warning);
		return 1;
	}
	inuse++;

	/* Require at least one argument */
	if (argc == 0) {
		luaPushError(lua,
			"Please specify at least one argument for rlite.call()");
		inuse--;
		return 1;
	}

	/* Build the arguments vector */
	if (argv_size < argc) {
		argv = realloc(argv, sizeof(char *) * argc);
		argvlen = realloc(argvlen, sizeof(size_t) * argc);
		argv_size = argc;
	}

	for (j = 0; j < argc; j++) {
		char *obj_s;
		size_t obj_len;
		char dbuf[64];

		if (lua_type(lua,j+1) == LUA_TNUMBER) {
			/* We can't use lua_tolstring() for number -> string conversion
			 * since Lua uses a format specifier that loses precision. */
			lua_Number num = lua_tonumber(lua,j+1);

			obj_len = snprintf(dbuf,sizeof(dbuf),"%.17g",(double)num);
			obj_s = dbuf;
		} else {
			obj_s = (char*)lua_tolstring(lua,j+1,&obj_len);
			if (obj_s == NULL) break; /* Not a string. */
		}

		argv[j] = obj_s;
		argvlen[j] = obj_len;
	}

	/* Check if one of the arguments passed by the Lua script
	 * is not a string or an integer (lua_isstring() return true for
	 * integers as well). */
	if (j != argc) {
		j--;
		while (j >= 0) {
			free(argv[j]);
			j--;
		}
		luaPushError(lua,
			"Lua rlite() command arguments must be strings or integers");
		inuse--;
		return 1;
	}

	/* Setup our fake client for command execution */
	c->argvlen = argvlen;
	c->argv = argv;
	c->argc = argc;

	/* Command lookup */
	cmd = rliteLookupCommand(argv[0], argvlen[0]);
	if (!cmd || ((cmd->arity > 0 && cmd->arity != argc) ||
				   (argc < -cmd->arity)))
	{
		if (cmd)
			luaPushError(lua,
				"Wrong number of args calling Redis command From Lua script");
		else
			luaPushError(lua,"Unknown Redis command called from Lua script");
		goto cleanup;
	}

	/* There are commands that are not allowed inside scripts. */
	if (cmd->flags & RLITE_CMD_NOSCRIPT) {
		luaPushError(lua, "This Redis command is not allowed from scripts");
		goto cleanup;
	}

	/* Write commands are forbidden against read-only slaves, or if a
	 * command marked as non-deterministic was already called in the context
	 * of this script. */
	if (cmd->flags & RLITE_CMD_WRITE) {
		if (lua_random_dirty) {
			luaPushError(lua,
				"Write commands not allowed after non deterministic commands");
			goto cleanup;
		}
	}

	if (cmd->flags & RLITE_CMD_RANDOM) lua_random_dirty = 1;
	if (cmd->flags & RLITE_CMD_WRITE) lua_write_dirty = 1;

	/* Run the command */
	rliteAppendCommandClient(c);
	rliteGetReply(c->context, (void **)&reply);

	if (raise_error && reply->type != RLITE_REPLY_ERROR) raise_error = 0;
	rliteToLuaType(lua,reply);
	/* Sort the output array if needed, assuming it is a non-null multi bulk
	 * reply as expected. */
	if ((cmd->flags & RLITE_CMD_SORT_FOR_SCRIPT) &&
		(reply->type == RLITE_REPLY_ARRAY && reply->elements > 0)) {
			luaSortArray(lua);
	}
	rliteFreeReplyObject(reply);
cleanup:
	if (c->argv != argv) {
		free(c->argv);
		argv = NULL;
		argv_size = 0;
	}

	if (raise_error) {
		/* If we are here we should have an error in the stack, in the
		 * form of a table with an "err" field. Extract the string to
		 * return the plain error. */
		lua_pushstring(lua,"err");
		lua_gettable(lua,-2);
		inuse--;
		return lua_error(lua);
	}
	inuse--;
	return 1;
}