mysql2 3.23.5-canary.c86fe5a → 3.24.0

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.
@@ -2,6 +2,7 @@
2
2
 
3
3
  const Types = require('../constants/types');
4
4
  const Packet = require('../packets/packet');
5
+ const StringParser = require('../parsers/string.js');
5
6
  const {
6
7
  TypedParameter,
7
8
  encodeTypedParameter,
@@ -33,9 +34,7 @@ function toParameter(value, encoding, timezone, jsonAsString, hint) {
33
34
  }
34
35
  let type = Types.VAR_STRING;
35
36
  let length;
36
- let writer = function (value) {
37
- return Packet.prototype.writeLengthCodedString.call(this, value, encoding);
38
- };
37
+ let writer = Packet.prototype.writeLengthCodedBuffer;
39
38
  if (value !== null) {
40
39
  switch (typeof value) {
41
40
  case 'undefined':
@@ -86,9 +85,12 @@ function toParameter(value, encoding, timezone, jsonAsString, hint) {
86
85
  } else {
87
86
  value = '';
88
87
  type = Types.NULL;
88
+ length = 0;
89
+ writer = writeNothing;
89
90
  }
90
- if (!length) {
91
- length = Packet.lengthCodedStringLength(value, encoding);
91
+ if (length === undefined) {
92
+ value = StringParser.encode(value, encoding);
93
+ length = Packet.lengthCodedNumberLength(value.length) + value.length;
92
94
  }
93
95
  return {
94
96
  value,
@@ -100,4 +102,6 @@ function toParameter(value, encoding, timezone, jsonAsString, hint) {
100
102
  };
101
103
  }
102
104
 
105
+ function writeNothing() {}
106
+
103
107
  module.exports = { toParameter, isJSON };
@@ -5,6 +5,7 @@ const CommandCodes = require('../constants/commands');
5
5
  const ClientConstants = require('../constants/client');
6
6
  const Types = require('../constants/types');
7
7
  const Packet = require('../packets/packet');
8
+ const StringParser = require('../parsers/string.js');
8
9
  const CharsetToEncoding = require('../constants/charset_encodings.js');
9
10
  const { toParameter } = require('./encode_parameter.js');
10
11
 
@@ -97,7 +98,7 @@ class Execute {
97
98
  return { stmtId, flags, iterationCount, values };
98
99
  }
99
100
 
100
- _serializeToBuffer(buffer) {
101
+ toPacket() {
101
102
  const useQueryAttributes =
102
103
  this.clientFlags & ClientConstants.CLIENT_QUERY_ATTRIBUTES;
103
104
 
@@ -107,7 +108,53 @@ class Execute {
107
108
  const numAttrs = attrNames.length;
108
109
  const totalParams = numParams + numAttrs;
109
110
 
110
- const packet = new Packet(0, buffer, 0, buffer.length);
111
+ // packet header, command, statement id, cursor flags, iteration count
112
+ let length = 14;
113
+ if (useQueryAttributes) {
114
+ length += Packet.lengthCodedNumberLength(totalParams);
115
+ }
116
+
117
+ let allParams = null;
118
+ let attrNameBuffers = null;
119
+ if (totalParams > 0) {
120
+ allParams = new Array(totalParams);
121
+ for (let i = 0; i < numParams; i++) {
122
+ allParams[i] = toParameter(
123
+ this.parameters[i],
124
+ this.encoding,
125
+ this.timezone,
126
+ this.jsonAsString,
127
+ this.parameterDefinitions[i]
128
+ );
129
+ }
130
+ for (let i = 0; i < numAttrs; i++) {
131
+ allParams[numParams + i] = toParameter(
132
+ this.attributes[attrNames[i]],
133
+ this.encoding,
134
+ this.timezone
135
+ );
136
+ }
137
+
138
+ // null bitmap, new-params-bound flag, type and unsigned byte per parameter
139
+ length += ((totalParams + 7) >> 3) + 1 + totalParams * 2;
140
+ if (useQueryAttributes) {
141
+ // one empty length-coded name per bind parameter
142
+ length += numParams;
143
+ attrNameBuffers = new Array(numAttrs);
144
+ for (let i = 0; i < numAttrs; i++) {
145
+ const name = StringParser.encode(attrNames[i], this.encoding);
146
+ attrNameBuffers[i] = name;
147
+ length += Packet.lengthCodedNumberLength(name.length) + name.length;
148
+ }
149
+ }
150
+ for (let i = 0; i < totalParams; i++) {
151
+ if (!allParams[i].isNull) {
152
+ length += allParams[i].length;
153
+ }
154
+ }
155
+ }
156
+
157
+ const packet = new Packet(0, Buffer.allocUnsafe(length), 0, length);
111
158
  packet.offset = 4;
112
159
  packet.writeInt8(CommandCodes.STMT_EXECUTE);
113
160
  packet.writeInt32(this.id);
@@ -124,29 +171,11 @@ class Execute {
124
171
  }
125
172
 
126
173
  if (totalParams > 0) {
127
- const bindParams =
128
- numParams > 0
129
- ? this.parameters.map((v, i) =>
130
- toParameter(
131
- v,
132
- this.encoding,
133
- this.timezone,
134
- this.jsonAsString,
135
- this.parameterDefinitions[i]
136
- )
137
- )
138
- : [];
139
- const attrParams = attrNames.map((name) =>
140
- toParameter(this.attributes[name], this.encoding, this.timezone)
141
- );
142
- const allParams = bindParams.concat(attrParams);
143
-
144
- // null bitmap
145
174
  let bitmap = 0;
146
175
  let bitValue = 1;
147
- allParams.forEach((parameter) => {
148
- if (parameter.isNull) {
149
- bitmap += bitValue;
176
+ for (let i = 0; i < totalParams; i++) {
177
+ if (allParams[i].isNull) {
178
+ bitmap |= bitValue;
150
179
  }
151
180
  bitValue *= 2;
152
181
  if (bitValue === 256) {
@@ -154,38 +183,41 @@ class Execute {
154
183
  bitmap = 0;
155
184
  bitValue = 1;
156
185
  }
157
- });
186
+ }
158
187
  if (bitValue !== 1) {
159
188
  packet.writeInt8(bitmap);
160
189
  }
161
190
 
162
191
  packet.writeInt8(1); // new-params-bound-flag
163
192
 
164
- // types (and names for attributes)
165
- for (let i = 0; i < allParams.length; i++) {
166
- packet.writeInt8(allParams[i].type);
167
- packet.writeInt8(allParams[i].unsigned ? 0x80 : 0);
193
+ for (let i = 0; i < totalParams; i++) {
194
+ const parameter = allParams[i];
195
+ packet.writeInt8(parameter.type);
196
+ packet.writeInt8(parameter.unsigned ? 0x80 : 0);
168
197
  if (useQueryAttributes) {
169
- const name = i < numParams ? '' : attrNames[i - numParams];
170
- packet.writeLengthCodedString(name, this.encoding);
198
+ if (i < numParams) {
199
+ packet.writeInt8(0); // bind parameters have an empty name
200
+ } else {
201
+ packet.writeLengthCodedBuffer(attrNameBuffers[i - numParams]);
202
+ }
171
203
  }
172
204
  }
173
205
 
174
- // values
175
- allParams.forEach((parameter) => {
206
+ for (let i = 0; i < totalParams; i++) {
207
+ const parameter = allParams[i];
176
208
  if (!parameter.isNull) {
177
209
  parameter.writer.call(packet, parameter.value);
178
210
  }
179
- });
211
+ }
180
212
  }
181
213
 
214
+ if (packet.offset !== length) {
215
+ throw new Error(
216
+ `Internal error: COM_STMT_EXECUTE serialized ${packet.offset - 4} bytes, expected ${length - 4}`
217
+ );
218
+ }
182
219
  return packet;
183
220
  }
184
-
185
- toPacket() {
186
- const p = this._serializeToBuffer(Packet.MockBuffer());
187
- return this._serializeToBuffer(Buffer.allocUnsafe(p.offset));
188
- }
189
221
  }
190
222
 
191
223
  module.exports = Execute;
@@ -965,6 +965,14 @@ class Packet {
965
965
  this.offset += bytes;
966
966
  }
967
967
 
968
+ // must match writeTime's choice of encoding byte for byte
969
+ static timeLength({ days, hours, minutes, seconds, microseconds }) {
970
+ if (!days && !hours && !minutes && !seconds && !microseconds) {
971
+ return 1;
972
+ }
973
+ return microseconds ? 13 : 9;
974
+ }
975
+
968
976
  writeTime({ negative, days, hours, minutes, seconds, microseconds }) {
969
977
  if (!days && !hours && !minutes && !seconds && !microseconds) {
970
978
  this.writeInt8(0);
@@ -5,7 +5,6 @@ const CommandCode = require('../constants/commands.js');
5
5
  const StringParser = require('../parsers/string.js');
6
6
  const CharsetToEncoding = require('../constants/charset_encodings.js');
7
7
  const ClientConstants = require('../constants/client.js');
8
- const Types = require('../constants/types.js');
9
8
  const { toParameter } = require('./encode_parameter.js');
10
9
 
11
10
  class Query {
@@ -17,67 +16,6 @@ class Query {
17
16
  this.clientFlags = clientFlags || 0;
18
17
  }
19
18
 
20
- serializeToBuffer(buffer) {
21
- const useQueryAttributes =
22
- this.clientFlags & ClientConstants.CLIENT_QUERY_ATTRIBUTES;
23
- const sqlBuf = StringParser.encode(this.query, this.encoding);
24
- const packet = new Packet(0, buffer, 0, buffer.length);
25
- packet.offset = 4;
26
- packet.writeInt8(CommandCode.QUERY);
27
-
28
- if (useQueryAttributes) {
29
- const attrs = this.attributes;
30
- const names = attrs ? Object.keys(attrs) : [];
31
- const paramCount = names.length;
32
-
33
- packet.writeLengthCodedNumber(paramCount);
34
- packet.writeLengthCodedNumber(1); // parameter_set_count, always 1
35
-
36
- if (paramCount > 0) {
37
- const parameters = names.map((name) =>
38
- toParameter(attrs[name], this.encoding, 'local')
39
- );
40
-
41
- // null bitmap
42
- let bitmap = 0;
43
- let bitValue = 1;
44
- parameters.forEach((parameter) => {
45
- if (parameter.type === Types.NULL) {
46
- bitmap += bitValue;
47
- }
48
- bitValue *= 2;
49
- if (bitValue === 256) {
50
- packet.writeInt8(bitmap);
51
- bitmap = 0;
52
- bitValue = 1;
53
- }
54
- });
55
- if (bitValue !== 1) {
56
- packet.writeInt8(bitmap);
57
- }
58
-
59
- packet.writeInt8(1); // new_params_bind_flag
60
-
61
- // types and names
62
- for (let i = 0; i < paramCount; i++) {
63
- packet.writeInt8(parameters[i].type);
64
- packet.writeInt8(0); // unsigned flag
65
- packet.writeLengthCodedString(names[i], this.encoding);
66
- }
67
-
68
- // values
69
- parameters.forEach((parameter) => {
70
- if (parameter.type !== Types.NULL) {
71
- parameter.writer.call(packet, parameter.value);
72
- }
73
- });
74
- }
75
- }
76
-
77
- packet.writeBuffer(sqlBuf);
78
- return packet;
79
- }
80
-
81
19
  toPacket() {
82
20
  const useQueryAttributes =
83
21
  this.clientFlags & ClientConstants.CLIENT_QUERY_ATTRIBUTES;
@@ -119,9 +57,81 @@ class Query {
119
57
  return packet;
120
58
  }
121
59
 
122
- // dry run to calculate required buffer length
123
- const p = this.serializeToBuffer(Packet.MockBuffer());
124
- return this.serializeToBuffer(Buffer.allocUnsafe(p.offset));
60
+ const names = Object.keys(this.attributes);
61
+ const parameters = new Array(attributeCount);
62
+ const nameBuffers = new Array(attributeCount);
63
+
64
+ // packet header, command, parameter count, parameter_set_count (always
65
+ // the single-byte form), null bitmap, new_params_bind_flag, type and
66
+ // unsigned byte per parameter
67
+ let length =
68
+ 5 +
69
+ Packet.lengthCodedNumberLength(attributeCount) +
70
+ 1 +
71
+ ((attributeCount + 7) >> 3) +
72
+ 1 +
73
+ attributeCount * 2;
74
+ for (let i = 0; i < attributeCount; i++) {
75
+ parameters[i] = toParameter(
76
+ this.attributes[names[i]],
77
+ this.encoding,
78
+ 'local'
79
+ );
80
+ const name = StringParser.encode(names[i], this.encoding);
81
+ nameBuffers[i] = name;
82
+ length += Packet.lengthCodedNumberLength(name.length) + name.length;
83
+ if (!parameters[i].isNull) {
84
+ length += parameters[i].length;
85
+ }
86
+ }
87
+ const sqlBuffer = StringParser.encode(this.query, this.encoding);
88
+ length += sqlBuffer.length;
89
+
90
+ const packet = new Packet(0, Buffer.allocUnsafe(length), 0, length);
91
+ packet.offset = 4;
92
+ packet.writeInt8(CommandCode.QUERY);
93
+ packet.writeLengthCodedNumber(attributeCount);
94
+ packet.writeLengthCodedNumber(1); // parameter_set_count, always 1
95
+
96
+ let bitmap = 0;
97
+ let bitValue = 1;
98
+ for (let i = 0; i < attributeCount; i++) {
99
+ if (parameters[i].isNull) {
100
+ bitmap |= bitValue;
101
+ }
102
+ bitValue *= 2;
103
+ if (bitValue === 256) {
104
+ packet.writeInt8(bitmap);
105
+ bitmap = 0;
106
+ bitValue = 1;
107
+ }
108
+ }
109
+ if (bitValue !== 1) {
110
+ packet.writeInt8(bitmap);
111
+ }
112
+
113
+ packet.writeInt8(1); // new_params_bind_flag
114
+
115
+ for (let i = 0; i < attributeCount; i++) {
116
+ packet.writeInt8(parameters[i].type);
117
+ packet.writeInt8(parameters[i].unsigned ? 0x80 : 0);
118
+ packet.writeLengthCodedBuffer(nameBuffers[i]);
119
+ }
120
+
121
+ for (let i = 0; i < attributeCount; i++) {
122
+ if (!parameters[i].isNull) {
123
+ parameters[i].writer.call(packet, parameters[i].value);
124
+ }
125
+ }
126
+
127
+ packet.writeBuffer(sqlBuffer);
128
+
129
+ if (packet.offset !== length) {
130
+ throw new Error(
131
+ `Internal error: COM_QUERY serialized ${packet.offset - 4} bytes, expected ${length - 4}`
132
+ );
133
+ }
134
+ return packet;
125
135
  }
126
136
  }
127
137
 
@@ -2,6 +2,7 @@
2
2
 
3
3
  const Types = require('../constants/types.js');
4
4
  const Packet = require('./packet.js');
5
+ const StringParser = require('../parsers/string.js');
5
6
 
6
7
  const INTEGER_BYTES = {
7
8
  [Types.TINY]: 1,
@@ -223,31 +224,30 @@ function toTimeParts(value) {
223
224
  }
224
225
 
225
226
  function timeEncoder() {
226
- return (value) => ({
227
- value: toTimeParts(value),
228
- length: 13,
229
- writer(v) {
230
- this.writeTime(v);
231
- },
232
- });
227
+ return (value) => {
228
+ const parts = toTimeParts(value);
229
+ return {
230
+ value: parts,
231
+ length: Packet.timeLength(parts),
232
+ writer(v) {
233
+ this.writeTime(v);
234
+ },
235
+ };
236
+ };
233
237
  }
234
238
 
235
239
  function lengthCodedEncoder(encoding) {
236
240
  return (value) => {
237
- if (Buffer.isBuffer(value)) {
238
- return {
239
- value,
240
- length: Packet.lengthCodedNumberLength(value.length) + value.length,
241
- writer: Packet.prototype.writeLengthCodedBuffer,
242
- };
243
- }
244
- const str = typeof value === 'string' ? value : String(value);
241
+ const buffer = Buffer.isBuffer(value)
242
+ ? value
243
+ : StringParser.encode(
244
+ typeof value === 'string' ? value : String(value),
245
+ encoding
246
+ );
245
247
  return {
246
- value: str,
247
- length: Packet.lengthCodedStringLength(str, encoding),
248
- writer(v) {
249
- Packet.prototype.writeLengthCodedString.call(this, v, encoding);
250
- },
248
+ value: buffer,
249
+ length: Packet.lengthCodedNumberLength(buffer.length) + buffer.length,
250
+ writer: Packet.prototype.writeLengthCodedBuffer,
251
251
  };
252
252
  };
253
253
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mysql2",
3
- "version": "3.23.5-canary.c86fe5a",
3
+ "version": "3.24.0",
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",