Skip to content

Commit 5eb98e4

Browse files
committed
Merge pull request #733 from raydog/multi-buffer-fix
Fix for multi/exec logic with detect_buffers enabled
2 parents 08f1a4d + 19c5571 commit 5eb98e4

File tree

2 files changed

+109
-5
lines changed

2 files changed

+109
-5
lines changed

index.js

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -610,7 +610,7 @@ function try_callback(callback, reply) {
610610
function reply_to_object(reply) {
611611
var obj = {}, j, jl, key, val;
612612

613-
if (reply.length === 0) {
613+
if (reply.length === 0 || !Array.isArray(reply)) {
614614
return null;
615615
}
616616

@@ -648,7 +648,7 @@ RedisClient.prototype.return_reply = function (reply) {
648648
// If the "reply" here is actually a message received asynchronously due to a
649649
// pubsub subscription, don't pop the command queue as we'll only be consuming
650650
// the head command prematurely.
651-
if (Array.isArray(reply) && reply.length > 0 && reply[0]) {
651+
if (this.pub_sub_mode && Array.isArray(reply) && reply.length > 0 && reply[0]) {
652652
type = reply[0].toString();
653653
}
654654

@@ -672,7 +672,7 @@ RedisClient.prototype.return_reply = function (reply) {
672672

673673
if (command_obj && !command_obj.sub_command) {
674674
if (typeof command_obj.callback === "function") {
675-
if (this.options.detect_buffers && command_obj.buffer_args === false) {
675+
if (this.options.detect_buffers && command_obj.buffer_args === false && 'exec' !== command_obj.command.toLowerCase()) {
676676
// If detect_buffers option was specified, then the reply from the parser will be Buffers.
677677
// If this command did not use Buffer arguments, then convert the reply to Strings here.
678678
reply = reply_to_strings(reply);
@@ -1106,15 +1106,24 @@ Multi.prototype.HMSET = Multi.prototype.hmset;
11061106
Multi.prototype.exec = function (callback) {
11071107
var self = this;
11081108
var errors = [];
1109+
var wants_buffers = [];
11091110
// drain queue, callback will catch "QUEUED" or error
11101111
// TODO - get rid of all of these anonymous functions which are elegant but slow
11111112
this.queue.forEach(function (args, index) {
1112-
var command = args[0], obj;
1113+
var command = args[0], obj, i, il, buffer_args;
11131114
if (typeof args[args.length - 1] === "function") {
11141115
args = args.slice(1, -1);
11151116
} else {
11161117
args = args.slice(1);
11171118
}
1119+
// Keep track of who wants buffer responses:
1120+
buffer_args = false;
1121+
for (i = 0, il = args.length; i < il; i += 1) {
1122+
if (Buffer.isBuffer(args[i])) {
1123+
buffer_args = true;
1124+
}
1125+
}
1126+
wants_buffers.push(buffer_args);
11181127
if (args.length === 1 && Array.isArray(args[0])) {
11191128
args = args[0];
11201129
}
@@ -1149,12 +1158,18 @@ Multi.prototype.exec = function (callback) {
11491158
}
11501159
}
11511160

1152-
var i, il, reply, args;
1161+
var i, il, reply, to_buffer, args;
11531162

11541163
if (replies) {
11551164
for (i = 1, il = self.queue.length; i < il; i += 1) {
11561165
reply = replies[i - 1];
11571166
args = self.queue[i];
1167+
to_buffer = wants_buffers[i];
1168+
1169+
// If we asked for strings, even in detect_buffers mode, then return strings:
1170+
if (self._client.options.detect_buffers && to_buffer === false) {
1171+
replies[i - 1] = reply = reply_to_strings(reply);
1172+
}
11581173

11591174
// TODO - confusing and error-prone that hgetall is special cased in two places
11601175
if (reply && args[0].toLowerCase() === "hgetall") {

test/test.js

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -792,6 +792,95 @@ tests.detect_buffers = function () {
792792
});
793793
};
794794

795+
tests.detect_buffers_multi = function () {
796+
var name = "detect_buffers_multi", detect_client = redis.createClient({detect_buffers: true});
797+
798+
detect_client.on("ready", function () {
799+
// single Buffer or String
800+
detect_client.set("string key 1", "string value");
801+
detect_client.multi().get("string key 1").exec(require_string("string value", name));
802+
detect_client.multi().get(new Buffer("string key 1")).exec(function (err, reply) {
803+
assert.strictEqual(null, err, name);
804+
assert.strictEqual(1, reply.length, name);
805+
assert.strictEqual(true, Buffer.isBuffer(reply[0]), name);
806+
assert.strictEqual("<Buffer 73 74 72 69 6e 67 20 76 61 6c 75 65>", reply[0].inspect(), name);
807+
});
808+
809+
detect_client.hmset("hash key 2", "key 1", "val 1", "key 2", "val 2");
810+
// array of Buffers or Strings
811+
detect_client.multi().hmget("hash key 2", "key 1", "key 2").exec(function (err, reply) {
812+
assert.strictEqual(null, err, name);
813+
assert.strictEqual(true, Array.isArray(reply), name);
814+
assert.strictEqual(1, reply.length, name);
815+
assert.strictEqual(2, reply[0].length, name);
816+
assert.strictEqual("val 1", reply[0][0], name);
817+
assert.strictEqual("val 2", reply[0][1], name);
818+
});
819+
detect_client.multi().hmget(new Buffer("hash key 2"), "key 1", "key 2").exec(function (err, reply) {
820+
assert.strictEqual(null, err, name);
821+
assert.strictEqual(true, Array.isArray(reply));
822+
assert.strictEqual(1, reply.length, name);
823+
assert.strictEqual(2, reply[0].length, name);
824+
assert.strictEqual(true, Buffer.isBuffer(reply[0][0]));
825+
assert.strictEqual(true, Buffer.isBuffer(reply[0][1]));
826+
assert.strictEqual("<Buffer 76 61 6c 20 31>", reply[0][0].inspect(), name);
827+
assert.strictEqual("<Buffer 76 61 6c 20 32>", reply[0][1].inspect(), name);
828+
});
829+
830+
// array of strings with undefined values (repro #344)
831+
detect_client.multi().hmget("hash key 2", "key 3", "key 4").exec(function(err, reply) {
832+
assert.strictEqual(null, err, name);
833+
assert.strictEqual(true, Array.isArray(reply), name);
834+
assert.strictEqual(1, reply.length, name);
835+
assert.strictEqual(2, reply[0].length, name);
836+
assert.equal(null, reply[0][0], name);
837+
assert.equal(null, reply[0][1], name);
838+
});
839+
840+
// Object of Buffers or Strings
841+
detect_client.multi().hgetall("hash key 2").exec(function (err, reply) {
842+
assert.strictEqual(null, err, name);
843+
assert.strictEqual(1, reply.length, name);
844+
assert.strictEqual("object", typeof reply[0], name);
845+
assert.strictEqual(2, Object.keys(reply[0]).length, name);
846+
assert.strictEqual("val 1", reply[0]["key 1"], name);
847+
assert.strictEqual("val 2", reply[0]["key 2"], name);
848+
});
849+
detect_client.multi().hgetall(new Buffer("hash key 2")).exec(function (err, reply) {
850+
assert.strictEqual(null, err, name);
851+
assert.strictEqual(1, reply.length, name);
852+
assert.strictEqual("object", typeof reply, name);
853+
assert.strictEqual(2, Object.keys(reply[0]).length, name);
854+
assert.strictEqual(true, Buffer.isBuffer(reply[0]["key 1"]));
855+
assert.strictEqual(true, Buffer.isBuffer(reply[0]["key 2"]));
856+
assert.strictEqual("<Buffer 76 61 6c 20 31>", reply[0]["key 1"].inspect(), name);
857+
assert.strictEqual("<Buffer 76 61 6c 20 32>", reply[0]["key 2"].inspect(), name);
858+
});
859+
860+
// Can interleave string and buffer results:
861+
detect_client.multi()
862+
.hget("hash key 2", "key 1")
863+
.hget(new Buffer("hash key 2"), "key 1")
864+
.hget("hash key 2", new Buffer("key 2"))
865+
.hget("hash key 2", "key 2")
866+
.exec(function (err, reply) {
867+
assert.strictEqual(null, err, name);
868+
assert.strictEqual(true, Array.isArray(reply));
869+
assert.strictEqual(4, reply.length, name);
870+
assert.strictEqual("val 1", reply[0], name);
871+
assert.strictEqual(true, Buffer.isBuffer(reply[1]));
872+
assert.strictEqual("<Buffer 76 61 6c 20 31>", reply[1].inspect(), name);
873+
assert.strictEqual(true, Buffer.isBuffer(reply[2]));
874+
assert.strictEqual("<Buffer 76 61 6c 20 32>", reply[2].inspect(), name);
875+
assert.strictEqual("val 2", reply[3], name);
876+
});
877+
878+
detect_client.quit(function (err, res) {
879+
next(name);
880+
});
881+
});
882+
};
883+
795884
tests.socket_nodelay = function () {
796885
var name = "socket_nodelay", c1, c2, c3, ready_count = 0, quit_count = 0;
797886

0 commit comments

Comments
 (0)