Skip to content

Commit a8048a9

Browse files
committed
README,c,test: hiredis.status introduced, hiredis.{OK,QUEUED,PONG} deprecated
This solves (1) early binding problem when it was not possible to write e.g assert(conn:command("RETURN", "FOO") == hiredis.FOO) and (2) naming ambiguity problem, when Redis cound introduce a new status that will clash with lua-hiredis naming (e.g. a status, named `command`). All hiredis.REPLY_STATUS objects are now created and cached automatically by the MT "magic". hiredis.{OK,QUEUED,PONG} are now deprecated in favor of their hiredis.status.* counterparts.
1 parent df7304c commit a8048a9

File tree

3 files changed

+119
-85
lines changed

3 files changed

+119
-85
lines changed

README.md

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -50,24 +50,19 @@ Hiredis error codes (see docs), also available as `hiredis.ERR_<something>`:
5050
with type `hiredis.REPLY_STATUS`.
5151
Common status objects are available in hiredis module table:
5252

53-
* `hiredis.OK`
54-
* `hiredis.QUEUED`
55-
* `hiredis.PONG`
53+
* `hiredis.status.OK`
54+
* `hiredis.status.QUEUED`
55+
* `hiredis.status.PONG`
5656

5757
It is guaranteed that these object instances are always used
5858
for their corresponding statuses (so you can make a simple equality check).
59-
60-
For replies not listed above, check const-object type and value
61-
directly, or just unwrap it with `hiredis.unwrap`.
59+
The same is true for any other object in `hiredis.status` table.
6260

6361
Examples:
6462

65-
assert(conn:command("PING") == hiredis.PONG)
66-
67-
local reply = assert(conn:command("TYPE", "BADKEY"))
68-
assert(reply.type == hiredis.REPLY_STATUS and reply.name == "none")
69-
70-
assert(assert(hiredis.unwrap(conn:command("TYPE", "NAME"))) == "string")
63+
assert(conn:command("PING") == hiredis.status.PONG)
64+
assert(conn:command("SET", "NAME", "lua-hiredis") == hiredis.status.OK)
65+
assert(conn:command("TYPE", "NAME") == hiredis.status.string)
7166

7267
* `REDIS_REPLY_ERROR` is a const-object with type `hiredis.REPLY_ERROR`.
7368
Note that Redis server errors are returned as `REDIS_REPLY_ERROR` values,
@@ -96,6 +91,19 @@ Use `hiredis.unwrap_reply()` to convert const-object to regular Lua value.
9691
Note: Unwrapping is not done automatically to make array reply handling
9792
more straightforward.
9893

94+
Deprecated features
95+
-------------------
96+
97+
For backwards compatibility following status const-objects are aliased
98+
in the `hiredis` module table:
99+
100+
* `hiredis.OK = hiredis.status.OK`
101+
* `hiredis.QUEUED = hiredis.status.QUEUED`
102+
* `hiredis.PONG = hiredis.status.PONG`
103+
104+
These aliases will eventually be removed in one of future releases,
105+
so, please, update your code to use `hiredis.status.*` instead.
106+
99107
More information
100108
----------------
101109

src/lua-hiredis.c

Lines changed: 48 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ extern "C" {
2828

2929
#define LUAHIREDIS_CONN_MT "lua-hiredis.connection"
3030
#define LUAHIREDIS_CONST_MT "lua-hiredis.const"
31+
#define LUAHIREDIS_STATUS_MT "lua-hiredis.status"
3132

3233
#define LUAHIREDIS_MAXARGS (256)
3334

@@ -110,6 +111,32 @@ static int push_new_const(
110111
return 1;
111112
}
112113

114+
static int lstatus_index(lua_State * L)
115+
{
116+
size_t key_len = 0;
117+
const char * key = NULL;
118+
luaL_checktype(L, 1, LUA_TTABLE);
119+
key = luaL_checklstring(L, 2, &key_len);
120+
121+
push_new_const(
122+
L, key, key_len, REDIS_REPLY_STATUS /* status */
123+
);
124+
lua_rawset(L, 1); /* t[key] = status */
125+
126+
lua_pushlstring(L, key, key_len); /* Push the key again */
127+
lua_gettable(L, 1); /* return t[key] */
128+
129+
return 1;
130+
}
131+
132+
/* status API */
133+
static const struct luaL_reg STATUS_MT[] =
134+
{
135+
{ "__index", lstatus_index },
136+
137+
{ NULL, NULL }
138+
};
139+
113140
static const struct luahiredis_Enum Errors[] =
114141
{
115142
{ "ERR_IO", REDIS_ERR_IO },
@@ -219,39 +246,13 @@ static int push_reply(lua_State * L, redisReply * pReply)
219246
{
220247
case REDIS_REPLY_STATUS:
221248
lua_pushvalue(L, lua_upvalueindex(1)); /* M (module table) */
249+
lua_getfield(L, -1, "status"); /* status = M.status */
250+
lua_remove(L, -2); /* Remove module table from stack */
222251

223-
lua_pushlstring(L, pReply->str, pReply->len); /* status */
224-
lua_gettable(L, -2); /* M[status] */
252+
lua_pushlstring(L, pReply->str, pReply->len); /* name */
253+
lua_gettable(L, -2); /* status[name] */
225254

226-
if (lua_isnil(L, -1)) /* Not bothering with metatables */
227-
{
228-
lua_pop(L, 1); /* Pop nil */
229-
230-
/*
231-
* TODO: Following code is likely to be broken due to early binding
232-
* (imagine that RETURN is a command that returns given string
233-
* as a status):
234-
*
235-
* assert(conn:command("RETURN", "FOO") == hiredis.FOO)
236-
*
237-
* Here hiredis.FOO would be nil before conn:command() is called.
238-
*
239-
* Note that this is not relevant to the current Redis implementation
240-
* (that is 2.2 and before), since it seems that it wouldn't
241-
* return any status code except OK, QUEUED or PONG,
242-
* all of which are already covered.
243-
*/
244-
lua_pushlstring(L, pReply->str, pReply->len); /* status */
245-
push_new_const(
246-
L, pReply->str, pReply->len, REDIS_REPLY_STATUS /* const */
247-
);
248-
lua_settable(L, -3); /* M[status] = const */
249-
250-
lua_pushlstring(L, pReply->str, pReply->len); /* status */
251-
lua_gettable(L, -2); /* return M[status] */
252-
}
253-
254-
lua_remove(L, -2); /* Remove module table from stack */
255+
lua_remove(L, -2); /* Remove status table from stack */
255256

256257
break;
257258

@@ -586,14 +587,24 @@ LUALIB_API int luaopen_hiredis(lua_State * L)
586587
push_new_const(L, "NIL", 3, REDIS_REPLY_NIL);
587588
lua_setfield(L, -2, LUAHIREDIS_KEY_NIL);
588589

589-
push_new_const(L, "OK", 2, REDIS_REPLY_STATUS);
590-
lua_setfield(L, -2, "OK");
590+
lua_newtable(L); /* status */
591+
592+
if (luaL_newmetatable(L, LUAHIREDIS_STATUS_MT))
593+
{
594+
luaL_register(L, NULL, STATUS_MT);
595+
lua_pushliteral(L, LUAHIREDIS_STATUS_MT);
596+
lua_setfield(L, -2, "__metatable");
597+
}
598+
lua_setmetatable(L, -2);
591599

592-
push_new_const(L, "QUEUED", 6, REDIS_REPLY_STATUS);
593-
lua_setfield(L, -2, "QUEUED");
600+
lua_getfield(L, -1, "OK");
601+
lua_setfield(L, -3, "OK"); /* hiredis.OK = status.OK */
602+
lua_getfield(L, -1, "QUEUED");
603+
lua_setfield(L, -3, "QUEUED"); /* hiredis.QUEUED = status.QUEUED */
604+
lua_getfield(L, -1, "PONG");
605+
lua_setfield(L, -3, "PONG"); /* hiredis.PONG = status.PONG */
594606

595-
push_new_const(L, "PONG", 4, REDIS_REPLY_STATUS);
596-
lua_setfield(L, -2, "PONG");
607+
lua_setfield(L, -2, "status"); /* hiredis.status = status */
597608

598609
/*
599610
* Register functions

test/test.lua

Lines changed: 51 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -16,27 +16,36 @@ assert(type(assert(getmetatable(hiredis.NIL))) == "string")
1616

1717
--------------------------------------------------------------------------------
1818

19-
assert(type(hiredis.OK == "table"))
20-
assert(hiredis.OK.name == "OK")
21-
assert(hiredis.OK.type == hiredis.REPLY_STATUS)
22-
assert(tostring(hiredis.OK) == "OK")
23-
assert(getmetatable(hiredis.OK) == getmetatable(hiredis.NIL))
19+
assert(type(hiredis.status.OK == "table"))
20+
assert(hiredis.status.OK.name == "OK")
21+
assert(hiredis.status.OK.type == hiredis.REPLY_STATUS)
22+
assert(tostring(hiredis.status.OK) == "OK")
23+
assert(getmetatable(hiredis.status.OK) == getmetatable(hiredis.NIL))
24+
25+
-- deprecated backwards compatibility
26+
assert(hiredis.OK == hiredis.status.OK)
2427

2528
--------------------------------------------------------------------------------
2629

27-
assert(type(hiredis.QUEUED== "table"))
28-
assert(hiredis.QUEUED.name == "QUEUED")
29-
assert(hiredis.QUEUED.type == hiredis.REPLY_STATUS)
30-
assert(tostring(hiredis.QUEUED) == "QUEUED")
31-
assert(getmetatable(hiredis.QUEUED) == getmetatable(hiredis.NIL))
30+
assert(type(hiredis.status.QUEUED == "table"))
31+
assert(hiredis.status.QUEUED.name == "QUEUED")
32+
assert(hiredis.status.QUEUED.type == hiredis.REPLY_STATUS)
33+
assert(tostring(hiredis.status.QUEUED) == "QUEUED")
34+
assert(getmetatable(hiredis.status.QUEUED) == getmetatable(hiredis.NIL))
35+
36+
-- deprecated backwards compatibility
37+
assert(hiredis.QUEUED == hiredis.status.QUEUED)
3238

3339
--------------------------------------------------------------------------------
3440

35-
assert(type(hiredis.PONG== "table"))
36-
assert(hiredis.PONG.name == "PONG")
37-
assert(hiredis.PONG.type == hiredis.REPLY_STATUS)
38-
assert(tostring(hiredis.PONG) == "PONG")
39-
assert(getmetatable(hiredis.PONG) == getmetatable(hiredis.NIL))
41+
assert(type(hiredis.status.PONG == "table"))
42+
assert(hiredis.status.PONG.name == "PONG")
43+
assert(hiredis.status.PONG.type == hiredis.REPLY_STATUS)
44+
assert(tostring(hiredis.status.PONG) == "PONG")
45+
assert(getmetatable(hiredis.status.PONG) == getmetatable(hiredis.NIL))
46+
47+
-- deprecated backwards compatibility
48+
assert(hiredis.PONG == hiredis.status.PONG)
4049

4150
--------------------------------------------------------------------------------
4251

@@ -48,7 +57,7 @@ local conn = assert(hiredis.connect("localhost", 6379))
4857

4958
--------------------------------------------------------------------------------
5059

51-
assert(conn:command("PING") == hiredis.PONG)
60+
assert(conn:command("PING") == hiredis.status.PONG)
5261

5362
--------------------------------------------------------------------------------
5463

@@ -63,6 +72,7 @@ assert(T.type == hiredis.REPLY_STATUS)
6372
assert(T.name == "string")
6473

6574
assert(hiredis.unwrap_reply(T) == "string")
75+
assert(T == hiredis.status.string)
6676

6777
--------------------------------------------------------------------------------
6878

@@ -77,6 +87,7 @@ assert(T.type == hiredis.REPLY_STATUS)
7787
assert(T.name == "none")
7888

7989
assert(hiredis.unwrap_reply(T) == "none")
90+
assert(T == hiredis.status.none)
8091

8192
--------------------------------------------------------------------------------
8293

@@ -100,34 +111,38 @@ end
100111

101112
--------------------------------------------------------------------------------
102113

103-
assert(assert(conn:command("MULTI")) == hiredis.OK)
104-
assert(assert(conn:command("SET", "MYKEY1", "MYVALUE1")) == hiredis.QUEUED)
105-
assert(assert(conn:command("GET", "MYKEY1")) == hiredis.QUEUED)
114+
assert(assert(conn:command("MULTI")) == hiredis.status.OK)
115+
assert(
116+
assert(conn:command("SET", "MYKEY1", "MYVALUE1")) == hiredis.status.QUEUED
117+
)
118+
assert(assert(conn:command("GET", "MYKEY1")) == hiredis.status.QUEUED)
106119
local t = assert(conn:command("EXEC"))
107-
assert(t[1] == hiredis.OK)
120+
assert(t[1] == hiredis.status.OK)
108121
assert(t[2] == "MYVALUE1")
109122

110123
--------------------------------------------------------------------------------
111124

112125
-- Based on actual bug scenario.
113-
assert(assert(conn:command("MULTI")) == hiredis.OK)
114-
assert(assert(conn:command("GET", "MYKEY1")) == hiredis.QUEUED)
115-
assert(assert(conn:command("SET", "MYKEY1", "MYVALUE2")) == hiredis.QUEUED)
126+
assert(assert(conn:command("MULTI")) == hiredis.status.OK)
127+
assert(assert(conn:command("GET", "MYKEY1")) == hiredis.status.QUEUED)
128+
assert(
129+
assert(conn:command("SET", "MYKEY1", "MYVALUE2")) == hiredis.status.QUEUED
130+
)
116131
local t = assert(conn:command("EXEC"))
117132
assert(t[1] == "MYVALUE1")
118-
assert(t[2] == hiredis.OK)
133+
assert(t[2] == hiredis.status.OK)
119134

120135
--------------------------------------------------------------------------------
121136

122137
assert(conn:command("MULTI"))
123-
assert(assert(conn:command("GET", "MYKEY1")) == hiredis.QUEUED)
138+
assert(assert(conn:command("GET", "MYKEY1")) == hiredis.status.QUEUED)
124139

125140
local err = assert(conn:command("SET"))
126141
assert(err.type == hiredis.REPLY_ERROR)
127142
assert(err.name == "ERR wrong number of arguments for 'set' command")
128143
assert(tostring(err) == "ERR wrong number of arguments for 'set' command")
129144

130-
assert(assert(conn:command("GET", "MYKEY1")) == hiredis.QUEUED)
145+
assert(assert(conn:command("GET", "MYKEY1")) == hiredis.status.QUEUED)
131146
local t = assert(conn:command("EXEC"))
132147

133148
for i = 1, #t do
@@ -138,15 +153,15 @@ end
138153

139154
assert(conn:command("MULTI"))
140155

141-
assert(assert(conn:command("SET", "MYKEY2", 1)) == hiredis.QUEUED)
156+
assert(assert(conn:command("SET", "MYKEY2", 1)) == hiredis.status.QUEUED)
142157

143158
-- Wrong value type
144-
assert(assert(conn:command("SADD", "MYKEY1", "MYVAL")) == hiredis.QUEUED)
159+
assert(assert(conn:command("SADD", "MYKEY1", "MYVAL")) == hiredis.status.QUEUED)
145160

146-
assert(assert(conn:command("INCR", "MYKEY2")) == hiredis.QUEUED)
161+
assert(assert(conn:command("INCR", "MYKEY2")) == hiredis.status.QUEUED)
147162
local t = assert(conn:command("EXEC"))
148163

149-
assert(t[1] == hiredis.OK)
164+
assert(t[1] == hiredis.status.OK)
150165
assert(t[2].type == hiredis.REPLY_ERROR)
151166
assert(
152167
t[2].name == "ERR Operation against a key holding the wrong kind of value"
@@ -161,13 +176,13 @@ conn:append_command("SADD", "MYKEY1", "MYVAL")
161176
conn:append_command("INCR", "MYKEY2")
162177
conn:append_command("EXEC")
163178

164-
assert(assert(conn:get_reply()) == hiredis.OK) -- MULTI
165-
assert(assert(conn:get_reply()) == hiredis.QUEUED) -- SET
166-
assert(assert(conn:get_reply()) == hiredis.QUEUED) -- SADD
167-
assert(assert(conn:get_reply()) == hiredis.QUEUED) -- INCR
179+
assert(assert(conn:get_reply()) == hiredis.status.OK) -- MULTI
180+
assert(assert(conn:get_reply()) == hiredis.status.QUEUED) -- SET
181+
assert(assert(conn:get_reply()) == hiredis.status.QUEUED) -- SADD
182+
assert(assert(conn:get_reply()) == hiredis.status.QUEUED) -- INCR
168183
local t = assert(conn:get_reply()) -- EXEC
169184

170-
assert(t[1] == hiredis.OK)
185+
assert(t[1] == hiredis.status.OK)
171186
assert(t[2].type == hiredis.REPLY_ERROR)
172187
assert(
173188
t[2].name == "ERR Operation against a key holding the wrong kind of value"
@@ -285,7 +300,7 @@ do
285300
end
286301

287302
do
288-
local r = pack(hiredis.unwrap_reply(hiredis.OK))
303+
local r = pack(hiredis.unwrap_reply(hiredis.status.OK))
289304
assert(r.n == 2)
290305
assert(r[1] == "OK")
291306
assert(r[2] == hiredis.REPLY_STATUS)

0 commit comments

Comments
 (0)