Skip to content

Commit b8c3523

Browse files
committed
FEATURE: redisTabular: First incomplete version
1 parent 1f6c92e commit b8c3523

File tree

5 files changed

+602
-0
lines changed

5 files changed

+602
-0
lines changed

CMakeLists.txt

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
cmake_minimum_required(VERSION 3.9)
2+
project(redistabular VERSION 1.0.0 DESCRIPTION "Redis Tabular module")
3+
4+
add_library(redistabular SHARED
5+
module.c
6+
redismodule.h
7+
)
8+
# This to add -fPIC
9+
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
10+
11+
# This to remove the lib prefix
12+
set_target_properties(redistabular PROPERTIES PREFIX "")

Dockerfile

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
FROM redis:latest
2+
MAINTAINER Centreon "support@centreon.com"
3+
4+
ENV LIBDIR /usr/lib/redis/modules
5+
ADD build/redistabular.so "$LIBDIR/redistabular.so"
6+
7+
CMD ["redis-server", "--loadmodule", "/usr/lib/redis/modules/redistabular.so"]

module.c

+203
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
#include "redismodule.h"
2+
#include <string.h>
3+
#include <stdio.h>
4+
#include <stdlib.h>
5+
6+
static int block_size = 0;
7+
8+
static int le(RedisModuleString **array, char *type, int i, int j) {
9+
size_t len;
10+
for (int k = 0; k < block_size; ++k) {
11+
if (type[k] == 'a') {
12+
const char *ai = RedisModule_StringPtrLen(array[i + k], &len);
13+
const char *aj = RedisModule_StringPtrLen(array[j + k], &len);
14+
int cmp = strcmp(ai, aj);
15+
if (cmp)
16+
return cmp < 0 ? 1 : 0;
17+
}
18+
else { /* The onyl choice here is 'n' */
19+
long long ai, aj;
20+
if (RedisModule_StringToLongLong(array[i + k], &ai) == REDISMODULE_ERR)
21+
ai = 0;
22+
if (RedisModule_StringToLongLong(array[j + k], &aj) == REDISMODULE_ERR)
23+
aj = 0;
24+
return ai < aj;
25+
}
26+
}
27+
return 1;
28+
}
29+
30+
static void swap(RedisModuleString **array, int i, int j) {
31+
for (int k = 0; k < block_size; ++k) {
32+
RedisModuleString *tmp = array[i + k];
33+
array[i + k] = array[j + k];
34+
array[j + k] = tmp;
35+
}
36+
}
37+
38+
static int partition(RedisModuleString **array, char *type,
39+
int begin, int last) {
40+
int store_idx = begin;
41+
for (int i = begin; i < last; i += block_size) {
42+
if (le(array, type, i, last)) {
43+
swap(array, i, store_idx);
44+
store_idx += block_size;
45+
}
46+
}
47+
swap(array, store_idx, last);
48+
return store_idx;
49+
}
50+
51+
static void quick_sort(RedisModuleString **array, char *type,
52+
int begin, int last, int ldown, int lup) {
53+
int pivot_idx = 0;
54+
if (begin < last) {
55+
pivot_idx = partition(array, type, begin, last);
56+
if (pivot_idx - block_size >= ldown) {
57+
quick_sort(array, type, begin, pivot_idx - block_size, ldown, lup);
58+
}
59+
if (pivot_idx + block_size <= lup) {
60+
quick_sort(array, type, pivot_idx + block_size, last, ldown, lup);
61+
}
62+
}
63+
}
64+
65+
/**
66+
* An implementation of a sort function for the centreon web interface.
67+
* The first argument is a Redis set to sort
68+
* Following arguments are fields to sort, each one followed by ASC or DESC.
69+
*
70+
* @param ctx The Redis context
71+
* @param argv An array of arguments
72+
* @param argc The arguments count with the command. That is to say
73+
* "tabular.sort 2" gives argc=2
74+
*
75+
* @return REDISMODULE_ERR or REDISMODULE_OK
76+
*/
77+
int TabularSort_RedisCommand(RedisModuleCtx *ctx,
78+
RedisModuleString **argv,
79+
int argc) {
80+
long long key_count;
81+
long long first, last;
82+
83+
if (argc <= 5) {
84+
return RedisModule_WrongArity(ctx);
85+
}
86+
87+
RedisModuleString *set = argv[1];
88+
if (RedisModule_StringToLongLong(argv[2], &first) == REDISMODULE_ERR)
89+
return RedisModule_ReplyWithError(
90+
ctx,
91+
"Err: The second argument must be an integer");
92+
if (RedisModule_StringToLongLong(argv[3], &last) == REDISMODULE_ERR)
93+
return RedisModule_ReplyWithError(
94+
ctx,
95+
"Err: The third argument must be an integer");
96+
if (first > last) {
97+
long long tmp = first;
98+
first = last;
99+
last = tmp;
100+
}
101+
102+
/* A block contains each column asked in the command line + the field
103+
* name */
104+
block_size = argc - 4;
105+
if (block_size % 2)
106+
return RedisModule_ReplyWithError(
107+
ctx,
108+
"Err: For each column you must tell how to sort with ALPHA or NUM");
109+
/* We add one to store the field key at the last position of each block */
110+
block_size = block_size / 2 + 1;
111+
112+
char type[block_size];
113+
type[block_size - 1] = 'a';
114+
for (size_t i = 0; i < block_size - 1; ++i) {
115+
size_t len;
116+
const char *a = RedisModule_StringPtrLen(argv[5 + 2 * i], &len);
117+
if (strncasecmp(a, "ALPHA", len) == 0)
118+
type[i] = 'a';
119+
else if (strncasecmp(a, "NUM", len) == 0)
120+
type[i] = 'n';
121+
else
122+
return RedisModule_ReplyWithError(
123+
ctx,
124+
"Err: For each column you must tell how to sort with ALPHA or NUM");
125+
}
126+
127+
RedisModuleCallReply *reply = RedisModule_Call(ctx, "SCARD", "s", set);
128+
129+
if (RedisModule_CallReplyType(reply) != REDISMODULE_REPLY_INTEGER)
130+
return RedisModule_ReplyWithError(
131+
ctx,
132+
"Err: Unable to get the set card");
133+
int size = RedisModule_CallReplyInteger(reply) * block_size;
134+
RedisModule_FreeCallReply(reply);
135+
136+
RedisModuleString **array = RedisModule_Alloc(
137+
size * sizeof(char *));
138+
139+
reply = RedisModule_Call(ctx, "SMEMBERS", "s", set);
140+
for (size_t i = block_size - 1, j = 0; i < size; i += block_size, ++j) {
141+
size_t len;
142+
const char *str = RedisModule_CallReplyStringPtr(
143+
RedisModule_CallReplyArrayElement(reply, j), &len);
144+
array[i] = RedisModule_CreateString(ctx, str, len);
145+
}
146+
RedisModule_FreeCallReply(reply);
147+
148+
for (size_t i = 0; i < block_size - 1; ++i) {
149+
for (size_t j = 0; j < size; j += block_size) {
150+
size_t len;
151+
reply = RedisModule_Call(
152+
ctx, "HGET", "ss",
153+
array[j + block_size - 1], argv[4 + 2 * i]);
154+
const char *str = RedisModule_CallReplyStringPtr(reply, &len);
155+
array[j + i] = RedisModule_CreateString(ctx, str, len);
156+
RedisModule_FreeCallReply(reply);
157+
}
158+
}
159+
160+
size_t ldown = first * block_size;
161+
size_t lup = last * block_size;
162+
if (ldown >= size)
163+
ldown = size - block_size;
164+
if (lup < ldown)
165+
lup = ldown;
166+
if (lup >= size)
167+
lup = size - block_size;
168+
169+
quick_sort(
170+
array, type,
171+
0, size - block_size,
172+
ldown, lup);
173+
174+
RedisModule_FreeCallReply(reply);
175+
reply = RedisModule_Call(ctx,
176+
"UNLINK", "c", "service_sort");
177+
RedisModule_FreeCallReply(reply);
178+
long long w = 0;
179+
for (size_t i = ldown; i <= lup; i += block_size, ++w) {
180+
reply = RedisModule_Call(ctx, "ZADD", "cls",
181+
"services_sort", w, array[i + block_size - 1]);
182+
RedisModule_FreeCallReply(reply);
183+
}
184+
185+
for (size_t i = 0; i < size; ++i) {
186+
RedisModule_FreeString(ctx, array[i]);
187+
}
188+
RedisModule_Free(array);
189+
190+
RedisModule_ReplyWithSimpleString(ctx, "OK");
191+
return REDISMODULE_OK;
192+
}
193+
194+
int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
195+
if (RedisModule_Init(ctx, "tabular", 1, REDISMODULE_APIVER_1)
196+
== REDISMODULE_ERR) return REDISMODULE_ERR;
197+
198+
if (RedisModule_CreateCommand(ctx, "tabular.sort",
199+
TabularSort_RedisCommand, "readonly", 1, 1, 1) == REDISMODULE_ERR)
200+
return REDISMODULE_ERR;
201+
202+
return REDISMODULE_OK;
203+
}

0 commit comments

Comments
 (0)