Skip to content

Commit c4614d1

Browse files
committed
Fix receiving large text fields
fixes #922
1 parent 8ea9f69 commit c4614d1

File tree

3 files changed

+113
-13
lines changed

3 files changed

+113
-13
lines changed

Changes.md

+4
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ This file is a manually maintained list of changes for each release. Feel free
44
to add your changes here when sending pull requests. Also send corrections if
55
you spot any mistakes.
66

7+
## HEAD
8+
9+
* Fix receiving large text fields #922
10+
711
## v2.5.1 (2014-09-22)
812

913
* Fix `pool.end` race conditions #915

lib/protocol/Parser.js

+24-13
Original file line numberDiff line numberDiff line change
@@ -98,25 +98,36 @@ Parser.prototype.write = function(buffer) {
9898
}
9999
};
100100

101-
Parser.prototype.append = function(newBuffer) {
102-
// If resume() is called, we don't pass a buffer to write()
103-
if (!newBuffer) {
101+
Parser.prototype.append = function append(chunk) {
102+
if (!chunk || chunk.length === 0) {
104103
return;
105104
}
106105

107-
var oldBuffer = this._buffer;
108-
var bytesRemaining = this._bytesRemaining();
109-
var newLength = bytesRemaining + newBuffer.length;
106+
var buffer = chunk;
107+
var sliceEnd = this._buffer.length;
108+
var sliceStart = this._packetOffset === null
109+
? this._offset
110+
: this._packetOffset;
111+
var sliceLength = sliceEnd - sliceStart;
110112

111-
var combinedBuffer = (this._offset > newLength)
112-
? oldBuffer.slice(0, newLength)
113-
: new Buffer(newLength);
113+
if (sliceLength !== 0) {
114+
// Create a new Buffer
115+
buffer = new Buffer(sliceLength + chunk.length);
114116

115-
oldBuffer.copy(combinedBuffer, 0, this._offset);
116-
newBuffer.copy(combinedBuffer, bytesRemaining);
117+
// Copy data
118+
this._buffer.copy(buffer, 0, sliceStart, sliceEnd);
119+
chunk.copy(buffer, sliceLength);
120+
}
117121

118-
this._buffer = combinedBuffer;
119-
this._offset = 0;
122+
// Adjust data-tracking pointers
123+
this._buffer = buffer;
124+
this._offset = this._offset - sliceStart;
125+
this._packetEnd = this._packetEnd !== null
126+
? this._packetEnd - sliceStart
127+
: null;
128+
this._packetOffset = this._packetOffset !== null
129+
? this._packetOffset - sliceStart
130+
: null;
120131
};
121132

122133
Parser.prototype.pause = function() {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
var assert = require('assert');
2+
var common = require('../../common');
3+
var crypto = require('crypto');
4+
5+
common.getTestConnection(function (err, connection) {
6+
assert.ifError(err);
7+
8+
getMaxAllowedPacket(connection);
9+
});
10+
11+
12+
var oldMaxAllowedPacket;
13+
function getMaxAllowedPacket(connection) {
14+
connection.query('SHOW VARIABLES WHERE Variable_name = ?', ['max_allowed_packet'], function (err, rows) {
15+
assert.ifError(err);
16+
17+
oldMaxAllowedPacket = Number(rows[0].Value);
18+
19+
increaseMaxAllowedPacketIfNeeded(connection);
20+
});
21+
}
22+
23+
function increaseMaxAllowedPacketIfNeeded(connection) {
24+
// Our test generates a SQL query a few bytes larger than 16 MB, but lets
25+
// leave a little margin:
26+
var minMaxAllowedPacket = 20 * 1024 * 1024;
27+
28+
var newMaxAllowedPacket = (oldMaxAllowedPacket < minMaxAllowedPacket)
29+
? minMaxAllowedPacket
30+
: oldMaxAllowedPacket;
31+
32+
connection.query('SET GLOBAL max_allowed_packet = ?', [newMaxAllowedPacket], function (err, rows) {
33+
assert.ifError(err);
34+
35+
// We need to re-connect for this change to take effect, bah
36+
connection.end();
37+
connection = common.createConnection();
38+
39+
// We need to wait for the re-connect to happen before starting the actual
40+
// test. That's because our buffer to hex shim in 0.4.x takes ~12 sec on
41+
// TravisCI, causing a MySQL connection timeout otherwise.
42+
connection.connect(function (err) {
43+
assert.ifError(err);
44+
45+
triggerLargeQueryAndResponsePackets(connection);
46+
});
47+
});
48+
}
49+
50+
var length = (Math.pow(256, 3) / 2) + 10; // Half, because of hex encoding
51+
var random = crypto.pseudoRandomBytes || crypto.randomBytes; // Depends on node.js version
52+
var table = 'large_text_test';
53+
54+
function triggerLargeQueryAndResponsePackets(connection) {
55+
random(length, function (err, buf) {
56+
assert.ifError(err);
57+
assert.equal(buf.length, length);
58+
59+
common.useTestDb(connection);
60+
61+
connection.query([
62+
'CREATE TEMPORARY TABLE ?? (',
63+
'`id` int(11) unsigned NOT NULL AUTO_INCREMENT,',
64+
'`bt` longtext NOT NULL,',
65+
'PRIMARY KEY (`id`)',
66+
') ENGINE=InnoDB DEFAULT CHARSET=utf8'
67+
].join('\n'), [table], assert.ifError);
68+
69+
var text = buf.toString('hex');
70+
71+
connection.query('INSERT INTO ?? SET ?', [table, {bt: text}], assert.ifError);
72+
73+
connection.query('SELECT `id`, `bt` FROM ??', [table], function (err, rows) {
74+
assert.ifError(err);
75+
76+
connection.query('SET GLOBAL max_allowed_packet = ?', [oldMaxAllowedPacket], assert.ifError);
77+
connection.end(function (err) {
78+
assert.ifError(err);
79+
assert.equal(rows.length, 1);
80+
assert.equal(rows[0].bt.length, text.length);
81+
assert.equal(rows[0].bt, text);
82+
});
83+
});
84+
});
85+
}

0 commit comments

Comments
 (0)