add top clause
This commit is contained in:
parent
fd709d62f7
commit
730886651a
51
README.md
51
README.md
@ -3,7 +3,7 @@
|
|||||||
This module aims to provide simple DML to manipulate the hashes in REDIS for SQL users. It works as simple as you expected. It translates the input statement to a set of pure REDIS commands. It does not need nor generate any intermediate stuffs which occupied your storages. The target data is your hashes only.
|
This module aims to provide simple DML to manipulate the hashes in REDIS for SQL users. It works as simple as you expected. It translates the input statement to a set of pure REDIS commands. It does not need nor generate any intermediate stuffs which occupied your storages. The target data is your hashes only.
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
```bash
|
```sql
|
||||||
$ redis-cli
|
$ redis-cli
|
||||||
127.0.0.1:6379> hmset phonebook:0001 name "Peter Nelson" tel "1-456-1246-3421" birth "2019-10-01" pos 3 gender "M"
|
127.0.0.1:6379> hmset phonebook:0001 name "Peter Nelson" tel "1-456-1246-3421" birth "2019-10-01" pos 3 gender "M"
|
||||||
127.0.0.1:6379> hmset phonebook:0002 name "Betty Joan" tel "1-444-9999-1112" birth "2019-12-01" pos 1 gender "F"
|
127.0.0.1:6379> hmset phonebook:0002 name "Betty Joan" tel "1-444-9999-1112" birth "2019-12-01" pos 1 gender "F"
|
||||||
@ -23,7 +23,7 @@ $ redis-cli
|
|||||||
## Getting started
|
## Getting started
|
||||||
|
|
||||||
### Get the package and build the binary:
|
### Get the package and build the binary:
|
||||||
```bash
|
```sql
|
||||||
$ git clone https://github.com/cscan/dbx.git
|
$ git clone https://github.com/cscan/dbx.git
|
||||||
$ cd dbx/src && make
|
$ cd dbx/src && make
|
||||||
```
|
```
|
||||||
@ -33,17 +33,17 @@ This plugin library is written in pure C. A file dbx.so is built after successfu
|
|||||||
### Load the module in redis (3 ways)
|
### Load the module in redis (3 ways)
|
||||||
|
|
||||||
1. Load the module in CLI
|
1. Load the module in CLI
|
||||||
```bash
|
```sql
|
||||||
127.0.0.1:6379> module load /path/to/dbx.so
|
127.0.0.1:6379> module load /path/to/dbx.so
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Start the server with loadmodule argument
|
2. Start the server with loadmodule argument
|
||||||
```bash
|
```sql
|
||||||
$ redis-server --loadmodule /path/to/dbx.so
|
$ redis-server --loadmodule /path/to/dbx.so
|
||||||
```
|
```
|
||||||
|
|
||||||
3. Adding the following line in the file redis.conf and then restart the server
|
3. Adding the following line in the file redis.conf and then restart the server
|
||||||
```bash
|
```sql
|
||||||
loadmodule /path/to/dbx.so
|
loadmodule /path/to/dbx.so
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -53,7 +53,7 @@ If you still have problem in loading the module, please visit: https://redis.io/
|
|||||||
|
|
||||||
#### Select Statement
|
#### Select Statement
|
||||||
You may specify multiple fields separated by comma
|
You may specify multiple fields separated by comma
|
||||||
```bash
|
```sql
|
||||||
127.0.0.1:6379> dbx select name, gender, birth from phonebook
|
127.0.0.1:6379> dbx select name, gender, birth from phonebook
|
||||||
1) 1) name
|
1) 1) name
|
||||||
2) "Betty Joan"
|
2) "Betty Joan"
|
||||||
@ -82,7 +82,7 @@ You may specify multiple fields separated by comma
|
|||||||
```
|
```
|
||||||
|
|
||||||
"*" is support
|
"*" is support
|
||||||
```bash
|
```sql
|
||||||
127.0.0.1:6379> dbx select * from phonebook where birth > '2019-11-11'
|
127.0.0.1:6379> dbx select * from phonebook where birth > '2019-11-11'
|
||||||
1) 1) "name"
|
1) 1) "name"
|
||||||
2) "Betty Joan"
|
2) "Betty Joan"
|
||||||
@ -97,7 +97,7 @@ You may specify multiple fields separated by comma
|
|||||||
```
|
```
|
||||||
|
|
||||||
If you want to show the exact keys, you may try rowid()
|
If you want to show the exact keys, you may try rowid()
|
||||||
```bash
|
```sql
|
||||||
127.0.0.1:6379> dbx select rowid() from phonebook
|
127.0.0.1:6379> dbx select rowid() from phonebook
|
||||||
1) 1) rowid()
|
1) 1) rowid()
|
||||||
2) "phonebook:1588299191-764848276"
|
2) "phonebook:1588299191-764848276"
|
||||||
@ -110,7 +110,7 @@ If you want to show the exact keys, you may try rowid()
|
|||||||
```
|
```
|
||||||
|
|
||||||
The above is nearly like REDIS keys command
|
The above is nearly like REDIS keys command
|
||||||
```bash
|
```sql
|
||||||
127.0.0.1:6379> keys phonebook*
|
127.0.0.1:6379> keys phonebook*
|
||||||
1) "phonebook:1588298418-551514504"
|
1) "phonebook:1588298418-551514504"
|
||||||
2) "phonebook:1588299196-2115347437"
|
2) "phonebook:1588299196-2115347437"
|
||||||
@ -122,7 +122,7 @@ Each record is exactly a hash, you could use raw REDIS commands ``hget, hmget or
|
|||||||
|
|
||||||
#### Where Clause in Select Statement
|
#### Where Clause in Select Statement
|
||||||
Your could specify =, >, <, >=, <=, <>, != or like conditions in where clause. Now the module only support "and" to join multiple conditions.
|
Your could specify =, >, <, >=, <=, <>, != or like conditions in where clause. Now the module only support "and" to join multiple conditions.
|
||||||
```bash
|
```sql
|
||||||
127.0.0.1:6379> dbx select tel from phonebook where name like Son
|
127.0.0.1:6379> dbx select tel from phonebook where name like Son
|
||||||
1) 1) tel
|
1) 1) tel
|
||||||
2) "1-888-3333-1412"
|
2) "1-888-3333-1412"
|
||||||
@ -135,7 +135,7 @@ Your could specify =, >, <, >=, <=, <>, != or like conditions in where clause. N
|
|||||||
|
|
||||||
#### Order Clause in Select Statement
|
#### Order Clause in Select Statement
|
||||||
Ordering can be ascending or descending. All sortings are alpha-sort.
|
Ordering can be ascending or descending. All sortings are alpha-sort.
|
||||||
```bash
|
```sql
|
||||||
127.0.0.1:6379> dbx select * from phonebook order by pos asc
|
127.0.0.1:6379> dbx select * from phonebook order by pos asc
|
||||||
...
|
...
|
||||||
127.0.0.1:6379> dbx select * from phonebook order by pos desc
|
127.0.0.1:6379> dbx select * from phonebook order by pos desc
|
||||||
@ -144,12 +144,17 @@ Ordering can be ascending or descending. All sortings are alpha-sort.
|
|||||||
|
|
||||||
#### Into Clause in Select Statement
|
#### Into Clause in Select Statement
|
||||||
You could create another table by into clause.
|
You could create another table by into clause.
|
||||||
```bash
|
```sql
|
||||||
127.0.0.1:6379> dbx select * into testbook from phonebook
|
127.0.0.1:6379> dbx select * into testbook from phonebook
|
||||||
1) testbook:1588325407-1751904058
|
1) testbook:1588325407-1751904058
|
||||||
2) testbook:1588325407-1751904059
|
2) testbook:1588325407-1751904059
|
||||||
3) testbook:1588325407-1751904060
|
3) testbook:1588325407-1751904060
|
||||||
4) testbook:1588325407-1751904061
|
4) testbook:1588325407-1751904061
|
||||||
|
127.0.0.1:6379> keys testbook*
|
||||||
|
1) "testbook:1588325407-1751904061"
|
||||||
|
2) "testbook:1588325407-1751904059"
|
||||||
|
3) "testbook:1588325407-1751904058"
|
||||||
|
4) "testbook:1588325407-1751904060"
|
||||||
127.0.0.1:6379> dbx select * from testbook
|
127.0.0.1:6379> dbx select * from testbook
|
||||||
1) 1) "name"
|
1) 1) "name"
|
||||||
2) "Mattias Swensson"
|
2) "Mattias Swensson"
|
||||||
@ -195,7 +200,7 @@ You could create another table by into clause.
|
|||||||
|
|
||||||
#### Delete Statement
|
#### Delete Statement
|
||||||
You may also use Insert and Delete statement to operate the hash. If you does not provide the where clause, it will delete all the records of the specified key prefix. (i.e. phonebook)
|
You may also use Insert and Delete statement to operate the hash. If you does not provide the where clause, it will delete all the records of the specified key prefix. (i.e. phonebook)
|
||||||
```bash
|
```sql
|
||||||
127.0.0.1:6379> dbx delete from phonebook where gender = F
|
127.0.0.1:6379> dbx delete from phonebook where gender = F
|
||||||
(integer) 2
|
(integer) 2
|
||||||
127.0.0.1:6379> dbx delete from phonebook
|
127.0.0.1:6379> dbx delete from phonebook
|
||||||
@ -204,7 +209,7 @@ You may also use Insert and Delete statement to operate the hash. If you does no
|
|||||||
|
|
||||||
#### Insert Statement
|
#### Insert Statement
|
||||||
The module provide simple Insert statement which same as the function of the REDIS command hmset. It will append a random string to your provided key (i.e. phonebook). If operation is successful, it will return the key name.
|
The module provide simple Insert statement which same as the function of the REDIS command hmset. It will append a random string to your provided key (i.e. phonebook). If operation is successful, it will return the key name.
|
||||||
```bash
|
```sql
|
||||||
127.0.0.1:6379> dbx insert into phonebook (name,tel,birth,pos,gender) values ('Peter Nelson' ,1-456-1246-3421, 2019-10-01, 3, M)
|
127.0.0.1:6379> dbx insert into phonebook (name,tel,birth,pos,gender) values ('Peter Nelson' ,1-456-1246-3421, 2019-10-01, 3, M)
|
||||||
"phonebook:1588298418-551514504"
|
"phonebook:1588298418-551514504"
|
||||||
127.0.0.1:6379> dbx insert into phonebook (name,tel,birth,pos,gender) values ('Betty Joan' ,1-444-9999-1112, 2019-12-01, 1, F)
|
127.0.0.1:6379> dbx insert into phonebook (name,tel,birth,pos,gender) values ('Betty Joan' ,1-444-9999-1112, 2019-12-01, 1, F)
|
||||||
@ -213,15 +218,27 @@ The module provide simple Insert statement which same as the function of the RED
|
|||||||
"phonebook:1588299196-2115347437"
|
"phonebook:1588299196-2115347437"
|
||||||
127.0.0.1:6379> dbx insert into phonebook (name,tel,birth,pos,gender) values ('Mattias Swensson' ,1-888-3333-1412, 2017-06-30, 4, M)
|
127.0.0.1:6379> dbx insert into phonebook (name,tel,birth,pos,gender) values ('Mattias Swensson' ,1-888-3333-1412, 2017-06-30, 4, M)
|
||||||
"phonebook:1588299202-1052597574"
|
"phonebook:1588299202-1052597574"
|
||||||
|
127.0.0.1:6379> hgetall phonebook:1588298418-551514504
|
||||||
|
1) "name"
|
||||||
|
2) "Peter Nelson"
|
||||||
|
3) "tel"
|
||||||
|
4) "1-456-1246-3421"
|
||||||
|
5) "birth"
|
||||||
|
6) "2019-10-01"
|
||||||
|
7) "pos"
|
||||||
|
8) "3"
|
||||||
|
9) "gender"
|
||||||
|
10) "M"
|
||||||
|
127.0.0.1:6379>
|
||||||
```
|
```
|
||||||
Note that Redis requires at least one space after the single and double quoted arguments.
|
Note that Redis requires at least one space after the single and double quoted arguments.
|
||||||
Or you may quote the whole SQL statement as below:
|
Or you may quote the whole SQL statement as below:
|
||||||
```bash
|
```sql
|
||||||
127.0.0.1:6379> dbx "insert into phonebook (name,tel,birth,pos,gender) values ('Peter Nelson','1-456-1246-3421','2019-10-01',3, 'M')"
|
127.0.0.1:6379> dbx "insert into phonebook (name,tel,birth,pos,gender) values ('Peter Nelson','1-456-1246-3421','2019-10-01',3, 'M')"
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Issue command from BASH shell
|
#### Issue command from BASH shell
|
||||||
```bash
|
```sql
|
||||||
$ redis-cli dbx select "*" from phonebook where gender = M order by pos desc
|
$ redis-cli dbx select "*" from phonebook where gender = M order by pos desc
|
||||||
1) 1) "name"
|
1) 1) "name"
|
||||||
2) "Mattias Swensson"
|
2) "Mattias Swensson"
|
||||||
@ -248,7 +265,7 @@ $ redis-cli dbx select name from phonebook where tel like 9812
|
|||||||
2) "Bloody Mary"
|
2) "Bloody Mary"
|
||||||
```
|
```
|
||||||
Note that "*" requires double quoted otherwise it will pass all the filename in current directory. Of course you could quote the whole SQL statement.
|
Note that "*" requires double quoted otherwise it will pass all the filename in current directory. Of course you could quote the whole SQL statement.
|
||||||
```bash
|
```sql
|
||||||
$ redis-cli dbx "select * from phonebook where gender = M order by pos desc"
|
$ redis-cli dbx "select * from phonebook where gender = M order by pos desc"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
69
src/dbx.c
69
src/dbx.c
@ -220,7 +220,7 @@ Vector* splitWhereString(char *s) {
|
|||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
int processRecords(RedisModuleCtx *ctx, RedisModuleCallReply *keys, regex_t *r, Vector *vSelect, Vector *vWhere, char *intoKey) {
|
size_t processRecords(RedisModuleCtx *ctx, RedisModuleCallReply *keys, regex_t *r, Vector *vSelect, Vector *vWhere, char *intoKey, long top) {
|
||||||
size_t nKeys = RedisModule_CallReplyLength(keys);
|
size_t nKeys = RedisModule_CallReplyLength(keys);
|
||||||
size_t affected = 0;
|
size_t affected = 0;
|
||||||
for (size_t i = 0; i < nKeys; i++) {
|
for (size_t i = 0; i < nKeys; i++) {
|
||||||
@ -234,6 +234,8 @@ int processRecords(RedisModuleCtx *ctx, RedisModuleCallReply *keys, regex_t *r,
|
|||||||
else
|
else
|
||||||
showRecord(ctx, key, vSelect);
|
showRecord(ctx, key, vSelect);
|
||||||
affected++;
|
affected++;
|
||||||
|
top--;
|
||||||
|
printf("%zu\n", affected);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
RedisModule_FreeString(ctx, key);
|
RedisModule_FreeString(ctx, key);
|
||||||
@ -242,7 +244,7 @@ int processRecords(RedisModuleCtx *ctx, RedisModuleCallReply *keys, regex_t *r,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Create temporary set for sorting */
|
/* Create temporary set for sorting */
|
||||||
int buildSetByPattern(RedisModuleCtx *ctx, regex_t *r, char *setName, Vector *vWhere) {
|
size_t buildSetByPattern(RedisModuleCtx *ctx, regex_t *r, char *setName, Vector *vWhere) {
|
||||||
RedisModule_Call(ctx, "DEL", "c", setName);
|
RedisModule_Call(ctx, "DEL", "c", setName);
|
||||||
RedisModuleString *scursor = RedisModule_CreateStringFromLongLong(ctx, 0);
|
RedisModuleString *scursor = RedisModule_CreateStringFromLongLong(ctx, 0);
|
||||||
long long lcursor;
|
long long lcursor;
|
||||||
@ -282,6 +284,7 @@ int SelectCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
|
|||||||
return RedisModule_WrongArity(ctx);
|
return RedisModule_WrongArity(ctx);
|
||||||
|
|
||||||
// Table
|
// Table
|
||||||
|
long top = 0;
|
||||||
RedisModuleString *fromKeys;
|
RedisModuleString *fromKeys;
|
||||||
char intoKey[32] = "";
|
char intoKey[32] = "";
|
||||||
|
|
||||||
@ -327,10 +330,28 @@ int SelectCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
|
|||||||
}
|
}
|
||||||
switch(step) {
|
switch(step) {
|
||||||
case 0:
|
case 0:
|
||||||
|
if (strcmp("top", token) == 0) {
|
||||||
|
step = -2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
step = -1;
|
||||||
|
case -1:
|
||||||
|
if (strlen(token) > 512) {
|
||||||
|
RedisModule_ReplyWithError(ctx, "select arguments are too long");
|
||||||
|
return REDISMODULE_ERR;
|
||||||
|
}
|
||||||
|
strcat(stmSelect, token);
|
||||||
|
step = -3;
|
||||||
|
break;
|
||||||
|
case -2:
|
||||||
|
top = atol(token);
|
||||||
|
step = -1;
|
||||||
|
break;
|
||||||
|
case -3:
|
||||||
if (strcmp("into", token) == 0)
|
if (strcmp("into", token) == 0)
|
||||||
step = -1;
|
step = -4;
|
||||||
else if (strcmp("from", token) == 0)
|
else if (strcmp("from", token) == 0)
|
||||||
step = -3;
|
step = -6;
|
||||||
else {
|
else {
|
||||||
if (strlen(stmSelect) + strlen(token) > 512) {
|
if (strlen(stmSelect) + strlen(token) > 512) {
|
||||||
RedisModule_ReplyWithError(ctx, "select arguments are too long");
|
RedisModule_ReplyWithError(ctx, "select arguments are too long");
|
||||||
@ -339,35 +360,35 @@ int SelectCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
|
|||||||
strcat(stmSelect, token);
|
strcat(stmSelect, token);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case -1:
|
case -4:
|
||||||
// parse into clause, assume time+rand always is new key
|
// parse into clause, assume time+rand always is new key
|
||||||
strcpy(intoKey, token);
|
strcpy(intoKey, token);
|
||||||
step = -2;
|
step = -5;
|
||||||
break;
|
break;
|
||||||
case -2:
|
case -5:
|
||||||
if (strcmp("from", token) == 0)
|
if (strcmp("from", token) == 0)
|
||||||
step = -3;
|
step = -6;
|
||||||
else {
|
else {
|
||||||
RedisModule_ReplyWithError(ctx, "from keyword is expected");
|
RedisModule_ReplyWithError(ctx, "from keyword is expected");
|
||||||
return REDISMODULE_ERR;
|
return REDISMODULE_ERR;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case -3:
|
case -6:
|
||||||
// parse from clause
|
// parse from clause
|
||||||
fromKeys = RMUtil_CreateFormattedString(ctx, token);
|
fromKeys = RMUtil_CreateFormattedString(ctx, token);
|
||||||
step = 4;
|
step = 7;
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 7:
|
||||||
if (strcmp("where", token) == 0)
|
if (strcmp("where", token) == 0)
|
||||||
step = -5;
|
step = -8;
|
||||||
else if (strcmp("order", token) == 0)
|
else if (strcmp("order", token) == 0)
|
||||||
step = -7;
|
step = -10;
|
||||||
break;
|
break;
|
||||||
case -5:
|
case -8:
|
||||||
case 6:
|
case 9:
|
||||||
// parse where clause
|
// parse where clause
|
||||||
if (strcmp("order", token) == 0)
|
if (strcmp("order", token) == 0)
|
||||||
step = -7;
|
step = -10;
|
||||||
else if (strcmp("and", token) == 0)
|
else if (strcmp("and", token) == 0)
|
||||||
strcat(stmWhere, "&&");
|
strcat(stmWhere, "&&");
|
||||||
else {
|
else {
|
||||||
@ -378,19 +399,19 @@ int SelectCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
|
|||||||
char *p = token;
|
char *p = token;
|
||||||
while (*p++) *p = *p == 7? 32: *p;
|
while (*p++) *p = *p == 7? 32: *p;
|
||||||
strcat(stmWhere, token);
|
strcat(stmWhere, token);
|
||||||
step = 6;
|
step = 9;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case -7:
|
case -10:
|
||||||
if (strcmp("by", token) == 0)
|
if (strcmp("by", token) == 0)
|
||||||
step = -8;
|
step = -11;
|
||||||
else {
|
else {
|
||||||
RedisModule_ReplyWithError(ctx, "missing 'by' after order");
|
RedisModule_ReplyWithError(ctx, "missing 'by' after order");
|
||||||
return REDISMODULE_ERR;
|
return REDISMODULE_ERR;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case -8:
|
case -11:
|
||||||
case 9:
|
case 12:
|
||||||
// parse order clause
|
// parse order clause
|
||||||
if (strlen(stmOrder) + strlen(token) > 512) {
|
if (strlen(stmOrder) + strlen(token) > 512) {
|
||||||
RedisModule_ReplyWithError(ctx, "order arguments are too long");
|
RedisModule_ReplyWithError(ctx, "order arguments are too long");
|
||||||
@ -401,7 +422,7 @@ int SelectCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
|
|||||||
else
|
else
|
||||||
if (strcmp("asc", token) != 0)
|
if (strcmp("asc", token) != 0)
|
||||||
strcat(stmOrder, token);
|
strcat(stmOrder, token);
|
||||||
step = 9;
|
step = 12;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
token = strtok(NULL, " ");
|
token = strtok(NULL, " ");
|
||||||
@ -447,7 +468,7 @@ int SelectCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
|
|||||||
sprintf(stmt, "*->%s", field);
|
sprintf(stmt, "*->%s", field);
|
||||||
rep = RedisModule_Call(ctx, "SORT", "cccc", setName, "by", stmt, "alpha");
|
rep = RedisModule_Call(ctx, "SORT", "cccc", setName, "by", stmt, "alpha");
|
||||||
}
|
}
|
||||||
size_t n = processRecords(ctx, rep, ®ex, vSelect, NULL, intoKey);
|
size_t n = processRecords(ctx, rep, ®ex, vSelect, NULL, intoKey, top);
|
||||||
RedisModule_FreeCallReply(rep);
|
RedisModule_FreeCallReply(rep);
|
||||||
|
|
||||||
// set number of output
|
// set number of output
|
||||||
@ -473,7 +494,7 @@ int SelectCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
|
|||||||
/* Filter by pattern matching. */
|
/* Filter by pattern matching. */
|
||||||
RedisModuleCallReply *rkeys = RedisModule_CallReplyArrayElement(rep, 1);
|
RedisModuleCallReply *rkeys = RedisModule_CallReplyArrayElement(rep, 1);
|
||||||
|
|
||||||
n += processRecords(ctx, rkeys, ®ex, vSelect, vWhere, intoKey);
|
n += processRecords(ctx, rkeys, ®ex, vSelect, vWhere, intoKey, top);
|
||||||
|
|
||||||
RedisModule_FreeCallReply(rep);
|
RedisModule_FreeCallReply(rep);
|
||||||
} while (lcursor);
|
} while (lcursor);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user