mysql2 3.24.0 → 3.24.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -88,14 +88,28 @@ function toParameter(value, encoding, timezone, jsonAsString, hint) {
88
88
  length = 0;
89
89
  writer = writeNothing;
90
90
  }
91
+ let byteLength;
91
92
  if (length === undefined) {
92
- value = StringParser.encode(value, encoding);
93
- length = Packet.lengthCodedNumberLength(value.length) + value.length;
93
+ // a non-string here (e.g. a Uint8Array) keeps the Buffer.from coercion
94
+ // inside StringParser.encode
95
+ if (
96
+ typeof value === 'string' &&
97
+ StringParser.hasFastUtf8Write &&
98
+ (encoding === 'utf8' || encoding === 'utf-8')
99
+ ) {
100
+ byteLength = Buffer.byteLength(value, 'utf8');
101
+ length = Packet.lengthCodedNumberLength(byteLength) + byteLength;
102
+ writer = Packet.prototype.writeLengthCodedUtf8String;
103
+ } else {
104
+ value = StringParser.encode(value, encoding);
105
+ length = Packet.lengthCodedNumberLength(value.length) + value.length;
106
+ }
94
107
  }
95
108
  return {
96
109
  value,
97
110
  type,
98
111
  length,
112
+ byteLength,
99
113
  writer,
100
114
  unsigned: false,
101
115
  isNull: type === Types.NULL,
@@ -206,7 +206,7 @@ class Execute {
206
206
  for (let i = 0; i < totalParams; i++) {
207
207
  const parameter = allParams[i];
208
208
  if (!parameter.isNull) {
209
- parameter.writer.call(packet, parameter.value);
209
+ parameter.writer.call(packet, parameter.value, parameter.byteLength);
210
210
  }
211
211
  }
212
212
  }
@@ -1030,6 +1030,18 @@ class Packet {
1030
1030
  this.offset += buf.length;
1031
1031
  }
1032
1032
 
1033
+ // byteLength, when the caller sized the packet, must be
1034
+ // Buffer.byteLength(string, 'utf8'); only used where
1035
+ // Buffer.prototype.utf8Write exists
1036
+ writeLengthCodedUtf8String(string, byteLength) {
1037
+ if (byteLength === undefined) {
1038
+ byteLength = Buffer.byteLength(string, 'utf8');
1039
+ }
1040
+ this.writeLengthCodedNumber(byteLength);
1041
+ this.buffer.utf8Write(string, this.offset, byteLength);
1042
+ this.offset += byteLength;
1043
+ }
1044
+
1033
1045
  writeLengthCodedBuffer(b) {
1034
1046
  this.writeLengthCodedNumber(b.length);
1035
1047
  b.copy(this.buffer, this.offset);
@@ -13,6 +13,18 @@ class PrepareStatement {
13
13
  }
14
14
 
15
15
  toPacket() {
16
+ if (
17
+ StringParser.hasFastUtf8Write &&
18
+ (this.encoding === 'utf8' || this.encoding === 'utf-8')
19
+ ) {
20
+ const length = 5 + Buffer.byteLength(this.query, 'utf8');
21
+ const buffer = Buffer.allocUnsafe(length);
22
+ buffer[4] = CommandCodes.STMT_PREPARE;
23
+ buffer.utf8Write(this.query, 5, length - 5);
24
+ const packet = new Packet(0, buffer, 0, length);
25
+ packet.offset = length;
26
+ return packet;
27
+ }
16
28
  const buf = StringParser.encode(this.query, this.encoding);
17
29
  const length = 5 + buf.length;
18
30
  const buffer = Buffer.allocUnsafe(length);
@@ -7,6 +7,19 @@ const CharsetToEncoding = require('../constants/charset_encodings.js');
7
7
  const ClientConstants = require('../constants/client.js');
8
8
  const { toParameter } = require('./encode_parameter.js');
9
9
 
10
+ const { hasFastUtf8Write } = StringParser;
11
+
12
+ function toQueryPacket(buffer, headerLength, length) {
13
+ buffer[4] = CommandCode.QUERY;
14
+ if (headerLength === 7) {
15
+ buffer[5] = 0; // parameter_count
16
+ buffer[6] = 1; // parameter_set_count, always 1
17
+ }
18
+ const packet = new Packet(0, buffer, 0, length);
19
+ packet.offset = length;
20
+ return packet;
21
+ }
22
+
10
23
  class Query {
11
24
  constructor(sql, charsetNumber, attributes, clientFlags) {
12
25
  this.query = sql;
@@ -25,36 +38,30 @@ class Query {
25
38
  : 0;
26
39
 
27
40
  if (attributeCount === 0) {
28
- // fast path: no attribute values to serialize, so the packet length is
29
- // known upfront and the SQL can be encoded straight into the buffer
41
+ // fast path: no attribute values to serialize, so the packet is the
42
+ // header plus the encoded SQL
30
43
  const headerLength = useQueryAttributes ? 7 : 5;
44
+ if (
45
+ hasFastUtf8Write &&
46
+ (this.encoding === 'utf8' || this.encoding === 'utf-8')
47
+ ) {
48
+ const length = headerLength + Buffer.byteLength(this.query, 'utf8');
49
+ const buffer = Buffer.allocUnsafe(length);
50
+ buffer.utf8Write(this.query, headerLength, length - headerLength);
51
+ return toQueryPacket(buffer, headerLength, length);
52
+ }
31
53
  if (Buffer.isEncoding(this.encoding)) {
32
- const sqlLength = Buffer.byteLength(this.query, this.encoding);
33
- const length = headerLength + sqlLength;
54
+ const length =
55
+ headerLength + Buffer.byteLength(this.query, this.encoding);
34
56
  const buffer = Buffer.allocUnsafe(length);
35
- const packet = new Packet(0, buffer, 0, length);
36
- packet.offset = 4;
37
- packet.writeInt8(CommandCode.QUERY);
38
- if (useQueryAttributes) {
39
- packet.writeInt8(0); // parameter_count
40
- packet.writeInt8(1); // parameter_set_count, always 1
41
- }
42
- buffer.write(this.query, packet.offset, this.encoding);
43
- packet.offset = length;
44
- return packet;
57
+ buffer.write(this.query, headerLength, this.encoding);
58
+ return toQueryPacket(buffer, headerLength, length);
45
59
  }
46
- const buf = StringParser.encode(this.query, this.encoding);
47
- const length = headerLength + buf.length;
60
+ const sqlBuffer = StringParser.encode(this.query, this.encoding);
61
+ const length = headerLength + sqlBuffer.length;
48
62
  const buffer = Buffer.allocUnsafe(length);
49
- const packet = new Packet(0, buffer, 0, length);
50
- packet.offset = 4;
51
- packet.writeInt8(CommandCode.QUERY);
52
- if (useQueryAttributes) {
53
- packet.writeInt8(0); // parameter_count
54
- packet.writeInt8(1); // parameter_set_count, always 1
55
- }
56
- packet.writeBuffer(buf);
57
- return packet;
63
+ sqlBuffer.copy(buffer, headerLength);
64
+ return toQueryPacket(buffer, headerLength, length);
58
65
  }
59
66
 
60
67
  const names = Object.keys(this.attributes);
@@ -120,7 +127,11 @@ class Query {
120
127
 
121
128
  for (let i = 0; i < attributeCount; i++) {
122
129
  if (!parameters[i].isNull) {
123
- parameters[i].writer.call(packet, parameters[i].value);
130
+ parameters[i].writer.call(
131
+ packet,
132
+ parameters[i].value,
133
+ parameters[i].byteLength
134
+ );
124
135
  }
125
136
  }
126
137
 
@@ -238,15 +238,25 @@ function timeEncoder() {
238
238
 
239
239
  function lengthCodedEncoder(encoding) {
240
240
  return (value) => {
241
- const buffer = Buffer.isBuffer(value)
242
- ? value
243
- : StringParser.encode(
244
- typeof value === 'string' ? value : String(value),
245
- encoding
246
- );
241
+ if (!Buffer.isBuffer(value)) {
242
+ const string = typeof value === 'string' ? value : String(value);
243
+ if (
244
+ StringParser.hasFastUtf8Write &&
245
+ (encoding === 'utf8' || encoding === 'utf-8')
246
+ ) {
247
+ const byteLength = Buffer.byteLength(string, 'utf8');
248
+ return {
249
+ value: string,
250
+ length: Packet.lengthCodedNumberLength(byteLength) + byteLength,
251
+ byteLength,
252
+ writer: Packet.prototype.writeLengthCodedUtf8String,
253
+ };
254
+ }
255
+ value = StringParser.encode(string, encoding);
256
+ }
247
257
  return {
248
- value: buffer,
249
- length: Packet.lengthCodedNumberLength(buffer.length) + buffer.length,
258
+ value,
259
+ length: Packet.lengthCodedNumberLength(value.length) + value.length,
250
260
  writer: Packet.prototype.writeLengthCodedBuffer,
251
261
  };
252
262
  };
@@ -15,6 +15,10 @@ const hasFastSlices =
15
15
  typeof Buffer.prototype.latin1Slice === 'function' &&
16
16
  typeof Buffer.prototype.asciiSlice === 'function';
17
17
 
18
+ // utf8Write skips the per-call encoding normalization and dispatch inside
19
+ // buffer.write(); same stability story as the slice methods above
20
+ exports.hasFastUtf8Write = typeof Buffer.prototype.utf8Write === 'function';
21
+
18
22
  exports.decode = function (buffer, encoding, start, end, options) {
19
23
  if (hasFastSlices) {
20
24
  // replicate buffer.toString() bounds coercion exactly (the *Slice
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mysql2",
3
- "version": "3.24.0",
3
+ "version": "3.24.1",
4
4
  "description": "fast mysql driver. Implements core protocol, prepared statements, ssl and compression in native JS",
5
5
  "main": "index.js",
6
6
  "typings": "typings/mysql/index",