mysql2 3.12.0 → 3.12.1-canary.51da6534

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.
@@ -5,6 +5,7 @@ const Query = require('./query.js');
5
5
  const Packets = require('../packets/index.js');
6
6
 
7
7
  const getBinaryParser = require('../parsers/binary_parser.js');
8
+ const getStaticBinaryParser = require('../parsers/static_binary_parser.js');
8
9
 
9
10
  class Execute extends Command {
10
11
  constructor(options, callback) {
@@ -25,12 +26,16 @@ class Execute extends Command {
25
26
  this._executeOptions = options;
26
27
  this._resultIndex = 0;
27
28
  this._localStream = null;
28
- this._unpipeStream = function() {};
29
+ this._unpipeStream = function () {};
29
30
  this._streamFactory = options.infileStreamFactory;
30
31
  this._connection = null;
31
32
  }
32
33
 
33
34
  buildParserFromFields(fields, connection) {
35
+ if (this.options.disableEval) {
36
+ return getStaticBinaryParser(fields, this.options, connection.config);
37
+ }
38
+
34
39
  return getBinaryParser(fields, this.options, connection.config);
35
40
  }
36
41
 
@@ -42,7 +47,7 @@ class Execute extends Command {
42
47
  this.statement.id,
43
48
  this.parameters,
44
49
  connection.config.charsetNumber,
45
- connection.config.timezone
50
+ connection.config.timezone,
46
51
  );
47
52
  //For reasons why this try-catch is here, please see
48
53
  // https://github.com/sidorares/node-mysql2/pull/689
@@ -68,7 +73,7 @@ class Execute extends Command {
68
73
  // this.statement.columns[this._receivedFieldsCount] : new Packets.ColumnDefinition(packet);
69
74
  const field = new Packets.ColumnDefinition(
70
75
  packet,
71
- connection.clientEncoding
76
+ connection.clientEncoding,
72
77
  );
73
78
  this._receivedFieldsCount++;
74
79
  this._fields[this._resultIndex].push(field);
@@ -87,7 +92,7 @@ class Execute extends Command {
87
92
  }
88
93
  this._rowParser = new (this.buildParserFromFields(
89
94
  this._fields[this._resultIndex],
90
- connection
95
+ connection,
91
96
  ))();
92
97
  return Execute.prototype.row;
93
98
  }
@@ -8,6 +8,7 @@ const Readable = require('stream').Readable;
8
8
  const Command = require('./command.js');
9
9
  const Packets = require('../packets/index.js');
10
10
  const getTextParser = require('../parsers/text_parser.js');
11
+ const staticParser = require('../parsers/static_text_parser.js');
11
12
  const ServerStatus = require('../constants/server_status.js');
12
13
 
13
14
  const EmptyPacket = new Packets.Packet(0, Buffer.allocUnsafe(4), 0, 4);
@@ -30,7 +31,7 @@ class Query extends Command {
30
31
  this._receivedFieldsCount = 0;
31
32
  this._resultIndex = 0;
32
33
  this._localStream = null;
33
- this._unpipeStream = function () { };
34
+ this._unpipeStream = function () {};
34
35
  this._streamFactory = options.infileStreamFactory;
35
36
  this._connection = null;
36
37
  }
@@ -55,7 +56,7 @@ class Query extends Command {
55
56
 
56
57
  const cmdPacket = new Packets.Query(
57
58
  this.sql,
58
- connection.config.charsetNumber
59
+ connection.config.charsetNumber,
59
60
  );
60
61
  connection.writePacket(cmdPacket.toPacket(1));
61
62
  return Query.prototype.resultsetHeader;
@@ -120,7 +121,7 @@ class Query extends Command {
120
121
  if (connection.config.debug) {
121
122
  // eslint-disable-next-line
122
123
  console.log(
123
- ` Resultset header received, expecting ${rs.fieldCount} column definition packets`
124
+ ` Resultset header received, expecting ${rs.fieldCount} column definition packets`,
124
125
  );
125
126
  }
126
127
  if (this._fieldCount === 0) {
@@ -140,7 +141,7 @@ class Query extends Command {
140
141
  this._localStream = this._streamFactory(path);
141
142
  } else {
142
143
  this._localStreamError = new Error(
143
- `As a result of LOCAL INFILE command server wants to read ${path} file, but as of v2.0 you must provide streamFactory option returning ReadStream.`
144
+ `As a result of LOCAL INFILE command server wants to read ${path} file, but as of v2.0 you must provide streamFactory option returning ReadStream.`,
144
145
  );
145
146
  connection.writePacket(EmptyPacket);
146
147
  return this.infileOk;
@@ -159,14 +160,14 @@ class Query extends Command {
159
160
  const dataWithHeader = Buffer.allocUnsafe(data.length + 4);
160
161
  data.copy(dataWithHeader, 4);
161
162
  connection.writePacket(
162
- new Packets.Packet(0, dataWithHeader, 0, dataWithHeader.length)
163
+ new Packets.Packet(0, dataWithHeader, 0, dataWithHeader.length),
163
164
  );
164
165
  };
165
166
  const onEnd = () => {
166
167
  connection.removeListener('error', onConnectionError);
167
168
  connection.writePacket(EmptyPacket);
168
169
  };
169
- const onError = err => {
170
+ const onError = (err) => {
170
171
  this._localStreamError = err;
171
172
  connection.removeListener('error', onConnectionError);
172
173
  connection.writePacket(EmptyPacket);
@@ -196,7 +197,7 @@ class Query extends Command {
196
197
  if (this._fields[this._resultIndex].length !== this._fieldCount) {
197
198
  const field = new Packets.ColumnDefinition(
198
199
  packet,
199
- connection.clientEncoding
200
+ connection.clientEncoding,
200
201
  );
201
202
  this._fields[this._resultIndex].push(field);
202
203
  if (connection.config.debug) {
@@ -212,7 +213,15 @@ class Query extends Command {
212
213
  if (this._receivedFieldsCount === this._fieldCount) {
213
214
  const fields = this._fields[this._resultIndex];
214
215
  this.emit('fields', fields);
215
- this._rowParser = new (getTextParser(fields, this.options, connection.config))(fields);
216
+ if (this.options.disableEval) {
217
+ this._rowParser = staticParser(fields, this.options, connection.config);
218
+ } else {
219
+ this._rowParser = new (getTextParser(
220
+ fields,
221
+ this.options,
222
+ connection.config,
223
+ ))(fields);
224
+ }
216
225
  return Query.prototype.fieldsEOF;
217
226
  }
218
227
  return Query.prototype.readField;
@@ -242,7 +251,7 @@ class Query extends Command {
242
251
  row = this._rowParser.next(
243
252
  packet,
244
253
  this._fields[this._resultIndex],
245
- this.options
254
+ this.options,
246
255
  );
247
256
  } catch (err) {
248
257
  this._localStreamError = err;
@@ -274,13 +283,13 @@ class Query extends Command {
274
283
  }
275
284
  stream.emit('result', row, resultSetIndex); // replicate old emitter
276
285
  });
277
- this.on('error', err => {
286
+ this.on('error', (err) => {
278
287
  stream.emit('error', err); // Pass on any errors
279
288
  });
280
289
  this.on('end', () => {
281
290
  stream.push(null); // pushing null, indicating EOF
282
291
  });
283
- this.on('fields', fields => {
292
+ this.on('fields', (fields) => {
284
293
  stream.emit('fields', fields); // replicate old emitter
285
294
  });
286
295
  stream.on('end', () => {
@@ -292,10 +301,7 @@ class Query extends Command {
292
301
  _setTimeout() {
293
302
  if (this.timeout) {
294
303
  const timeoutHandler = this._handleTimeoutError.bind(this);
295
- this.queryTimeout = Timers.setTimeout(
296
- timeoutHandler,
297
- this.timeout
298
- );
304
+ this.queryTimeout = Timers.setTimeout(timeoutHandler, this.timeout);
299
305
  }
300
306
  }
301
307
 
@@ -10,7 +10,7 @@
10
10
  const { URL } = require('url');
11
11
  const ClientConstants = require('./constants/client');
12
12
  const Charsets = require('./constants/charsets');
13
- const { version } = require('../package.json')
13
+ const { version } = require('../package.json');
14
14
  let SSLProfiles = null;
15
15
 
16
16
  const validOptions = {
@@ -59,6 +59,7 @@ const validOptions = {
59
59
  typeCast: 1,
60
60
  uri: 1,
61
61
  user: 1,
62
+ disableEval: 1,
62
63
  // These options are used for Pool
63
64
  connectionLimit: 1,
64
65
  maxIdle: 1,
@@ -66,7 +67,7 @@ const validOptions = {
66
67
  Promise: 1,
67
68
  queueLimit: 1,
68
69
  waitForConnections: 1,
69
- jsonStrings: 1
70
+ jsonStrings: 1,
70
71
  };
71
72
 
72
73
  class ConnectionConfig {
@@ -87,14 +88,17 @@ class ConnectionConfig {
87
88
  // REVIEW: Should this be emitted somehow?
88
89
  // eslint-disable-next-line no-console
89
90
  console.error(
90
- `Ignoring invalid configuration option passed to Connection: ${key}. This is currently a warning, but in future versions of MySQL2, an error will be thrown if you pass an invalid configuration option to a Connection`
91
+ `Ignoring invalid configuration option passed to Connection: ${key}. This is currently a warning, but in future versions of MySQL2, an error will be thrown if you pass an invalid configuration option to a Connection`,
91
92
  );
92
93
  }
93
94
  }
94
95
  this.isServer = options.isServer;
95
96
  this.stream = options.stream;
96
97
  this.host = options.host || 'localhost';
97
- this.port = (typeof options.port === 'string' ? parseInt(options.port, 10) : options.port)|| 3306;
98
+ this.port =
99
+ (typeof options.port === 'string'
100
+ ? parseInt(options.port, 10)
101
+ : options.port) || 3306;
98
102
  this.localAddress = options.localAddress;
99
103
  this.socketPath = options.socketPath;
100
104
  this.user = options.user || undefined;
@@ -128,7 +132,7 @@ class ConnectionConfig {
128
132
  // https://github.com/mysqljs/mysql#user-content-connection-options
129
133
  // eslint-disable-next-line no-console
130
134
  console.error(
131
- `Ignoring invalid timezone passed to Connection: ${options.timezone}. This is currently a warning, but in future versions of MySQL2, an error will be thrown if you pass an invalid configuration option to a Connection`
135
+ `Ignoring invalid timezone passed to Connection: ${options.timezone}. This is currently a warning, but in future versions of MySQL2, an error will be thrown if you pass an invalid configuration option to a Connection`,
132
136
  );
133
137
  // SqlStrings falls back to UTC on invalid timezone
134
138
  this.timezone = 'Z';
@@ -147,6 +151,7 @@ class ConnectionConfig {
147
151
  this.nestTables =
148
152
  options.nestTables === undefined ? undefined : options.nestTables;
149
153
  this.typeCast = options.typeCast === undefined ? true : options.typeCast;
154
+ this.disableEval = Boolean(options.disableEval);
150
155
  if (this.timezone[0] === ' ') {
151
156
  // "+" is a url encoded char for space so it
152
157
  // gets translated to space when giving a
@@ -156,7 +161,7 @@ class ConnectionConfig {
156
161
  if (this.ssl) {
157
162
  if (typeof this.ssl !== 'object') {
158
163
  throw new TypeError(
159
- `SSL profile must be an object, instead it's a ${typeof this.ssl}`
164
+ `SSL profile must be an object, instead it's a ${typeof this.ssl}`,
160
165
  );
161
166
  }
162
167
  // Default rejectUnauthorized to true
@@ -171,15 +176,18 @@ class ConnectionConfig {
171
176
  this.authSwitchHandler = options.authSwitchHandler;
172
177
  this.clientFlags = ConnectionConfig.mergeFlags(
173
178
  ConnectionConfig.getDefaultFlags(options),
174
- options.flags || ''
179
+ options.flags || '',
175
180
  );
176
181
  // Default connection attributes
177
182
  // https://dev.mysql.com/doc/refman/8.0/en/performance-schema-connection-attribute-tables.html
178
- const defaultConnectAttributes = {
183
+ const defaultConnectAttributes = {
179
184
  _client_name: 'Node-MySQL-2',
180
- _client_version: version
185
+ _client_version: version,
186
+ };
187
+ this.connectAttributes = {
188
+ ...defaultConnectAttributes,
189
+ ...(options.connectAttributes || {}),
181
190
  };
182
- this.connectAttributes = { ...defaultConnectAttributes, ...(options.connectAttributes || {})};
183
191
  this.maxPreparedStatements = options.maxPreparedStatements || 16000;
184
192
  this.jsonStrings = options.jsonStrings || false;
185
193
  }
@@ -229,7 +237,7 @@ class ConnectionConfig {
229
237
  'MULTI_RESULTS',
230
238
  'TRANSACTIONS',
231
239
  'SESSION_TRACK',
232
- 'CONNECT_ATTRS'
240
+ 'CONNECT_ATTRS',
233
241
  ];
234
242
  if (options && options.multipleStatements) {
235
243
  defaultFlags.push('MULTI_STATEMENTS');
package/lib/helpers.js CHANGED
@@ -74,14 +74,13 @@ const privateObjectProps = new Set([
74
74
 
75
75
  exports.privateObjectProps = privateObjectProps;
76
76
 
77
- const fieldEscape = (field) => {
77
+ const fieldEscape = (field, isEval = true) => {
78
78
  if (privateObjectProps.has(field)) {
79
79
  throw new Error(
80
80
  `The field name (${field}) can't be the same as an object's private property.`,
81
81
  );
82
82
  }
83
83
 
84
- return srcEscape(field);
84
+ return isEval ? srcEscape(field) : field;
85
85
  };
86
-
87
86
  exports.fieldEscape = fieldEscape;
@@ -0,0 +1,211 @@
1
+ 'use strict';
2
+
3
+ const FieldFlags = require('../constants/field_flags.js');
4
+ const Charsets = require('../constants/charsets.js');
5
+ const Types = require('../constants/types.js');
6
+ const helpers = require('../helpers');
7
+
8
+ const typeNames = [];
9
+ for (const t in Types) {
10
+ typeNames[Types[t]] = t;
11
+ }
12
+
13
+ function getBinaryParser(fields, _options, config) {
14
+ function readCode(field, config, options, fieldNum, packet) {
15
+ const supportBigNumbers = Boolean(
16
+ options.supportBigNumbers || config.supportBigNumbers,
17
+ );
18
+ const bigNumberStrings = Boolean(
19
+ options.bigNumberStrings || config.bigNumberStrings,
20
+ );
21
+ const timezone = options.timezone || config.timezone;
22
+ const dateStrings = options.dateStrings || config.dateStrings;
23
+ const unsigned = field.flags & FieldFlags.UNSIGNED;
24
+
25
+ switch (field.columnType) {
26
+ case Types.TINY:
27
+ return unsigned ? packet.readInt8() : packet.readSInt8();
28
+ case Types.SHORT:
29
+ return unsigned ? packet.readInt16() : packet.readSInt16();
30
+ case Types.LONG:
31
+ case Types.INT24: // in binary protocol int24 is encoded in 4 bytes int32
32
+ return unsigned ? packet.readInt32() : packet.readSInt32();
33
+ case Types.YEAR:
34
+ return packet.readInt16();
35
+ case Types.FLOAT:
36
+ return packet.readFloat();
37
+ case Types.DOUBLE:
38
+ return packet.readDouble();
39
+ case Types.NULL:
40
+ return null;
41
+ case Types.DATE:
42
+ case Types.DATETIME:
43
+ case Types.TIMESTAMP:
44
+ case Types.NEWDATE:
45
+ return helpers.typeMatch(field.columnType, dateStrings, Types)
46
+ ? packet.readDateTimeString(
47
+ parseInt(field.decimals, 10),
48
+ null,
49
+ field.columnType,
50
+ )
51
+ : packet.readDateTime(timezone);
52
+ case Types.TIME:
53
+ return packet.readTimeString();
54
+ case Types.DECIMAL:
55
+ case Types.NEWDECIMAL:
56
+ return config.decimalNumbers
57
+ ? packet.parseLengthCodedFloat()
58
+ : packet.readLengthCodedString('ascii');
59
+ case Types.GEOMETRY:
60
+ return packet.parseGeometryValue();
61
+ case Types.VECTOR:
62
+ return packet.parseVector();
63
+ case Types.JSON:
64
+ // Since for JSON columns mysql always returns charset 63 (BINARY),
65
+ // we have to handle it according to JSON specs and use "utf8",
66
+ // see https://github.com/sidorares/node-mysql2/issues/409
67
+ return config.jsonStrings
68
+ ? packet.readLengthCodedString('utf8')
69
+ : JSON.parse(packet.readLengthCodedString('utf8'));
70
+ case Types.LONGLONG:
71
+ if (!supportBigNumbers)
72
+ return unsigned
73
+ ? packet.readInt64JSNumber()
74
+ : packet.readSInt64JSNumber();
75
+ return bigNumberStrings
76
+ ? unsigned
77
+ ? packet.readInt64String()
78
+ : packet.readSInt64String()
79
+ : unsigned
80
+ ? packet.readInt64()
81
+ : packet.readSInt64();
82
+ default:
83
+ return field.characterSet === Charsets.BINARY
84
+ ? packet.readLengthCodedBuffer()
85
+ : packet.readLengthCodedString(fields[fieldNum].encoding);
86
+ }
87
+ }
88
+
89
+ return class BinaryRow {
90
+ constructor() {}
91
+
92
+ next(packet, fields, options) {
93
+ packet.readInt8(); // status byte
94
+
95
+ const nullBitmapLength = Math.floor((fields.length + 7 + 2) / 8);
96
+ const nullBitmaskBytes = new Array(nullBitmapLength);
97
+
98
+ for (let i = 0; i < nullBitmapLength; i++) {
99
+ nullBitmaskBytes[i] = packet.readInt8();
100
+ }
101
+
102
+ const result = options.rowsAsArray ? new Array(fields.length) : {};
103
+ let currentFieldNullBit = 4;
104
+ let nullByteIndex = 0;
105
+
106
+ for (let i = 0; i < fields.length; i++) {
107
+ const field = fields[i];
108
+ const typeCast =
109
+ options.typeCast !== undefined ? options.typeCast : config.typeCast;
110
+
111
+ let value;
112
+ if (nullBitmaskBytes[nullByteIndex] & currentFieldNullBit) {
113
+ value = null;
114
+ } else if (options.typeCast === false) {
115
+ value = packet.readLengthCodedBuffer();
116
+ } else {
117
+ const next = () => readCode(field, config, options, i, packet);
118
+ value =
119
+ typeof typeCast === 'function'
120
+ ? typeCast(
121
+ {
122
+ type: typeNames[field.columnType],
123
+ length: field.columnLength,
124
+ db: field.schema,
125
+ table: field.table,
126
+ name: field.name,
127
+ string: function (encoding = field.encoding) {
128
+ if (
129
+ field.columnType === Types.JSON &&
130
+ encoding === field.encoding
131
+ ) {
132
+ // Since for JSON columns mysql always returns charset 63 (BINARY),
133
+ // we have to handle it according to JSON specs and use "utf8",
134
+ // see https://github.com/sidorares/node-mysql2/issues/1661
135
+ console.warn(
136
+ `typeCast: JSON column "${field.name}" is interpreted as BINARY by default, recommended to manually set utf8 encoding: \`field.string("utf8")\``,
137
+ );
138
+ }
139
+
140
+ if (
141
+ [
142
+ Types.DATETIME,
143
+ Types.NEWDATE,
144
+ Types.TIMESTAMP,
145
+ Types.DATE,
146
+ ].includes(field.columnType)
147
+ ) {
148
+ return packet.readDateTimeString(
149
+ parseInt(field.decimals, 10),
150
+ );
151
+ }
152
+
153
+ if (field.columnType === Types.TINY) {
154
+ const unsigned = field.flags & FieldFlags.UNSIGNED;
155
+
156
+ return String(
157
+ unsigned ? packet.readInt8() : packet.readSInt8(),
158
+ );
159
+ }
160
+
161
+ if (field.columnType === Types.TIME) {
162
+ return packet.readTimeString();
163
+ }
164
+
165
+ return packet.readLengthCodedString(encoding);
166
+ },
167
+ buffer: function () {
168
+ return packet.readLengthCodedBuffer();
169
+ },
170
+ geometry: function () {
171
+ return packet.parseGeometryValue();
172
+ },
173
+ },
174
+ next,
175
+ )
176
+ : next();
177
+ }
178
+
179
+ if (options.rowsAsArray) {
180
+ result[i] = value;
181
+ } else if (typeof options.nestTables === 'string') {
182
+ const key = helpers.fieldEscape(
183
+ field.table + options.nestTables + field.name,
184
+ false,
185
+ );
186
+ result[key] = value;
187
+ } else if (options.nestTables === true) {
188
+ const tableName = helpers.fieldEscape(field.table, false);
189
+ if (!result[tableName]) {
190
+ result[tableName] = {};
191
+ }
192
+ const fieldName = helpers.fieldEscape(field.name, false);
193
+ result[tableName][fieldName] = value;
194
+ } else {
195
+ const key = helpers.fieldEscape(field.name, false);
196
+ result[key] = value;
197
+ }
198
+
199
+ currentFieldNullBit *= 2;
200
+ if (currentFieldNullBit === 0x100) {
201
+ currentFieldNullBit = 1;
202
+ nullByteIndex++;
203
+ }
204
+ }
205
+
206
+ return result;
207
+ }
208
+ };
209
+ }
210
+
211
+ module.exports = getBinaryParser;
@@ -0,0 +1,150 @@
1
+ 'use strict';
2
+
3
+ const Types = require('../constants/types.js');
4
+ const Charsets = require('../constants/charsets.js');
5
+ const helpers = require('../helpers');
6
+
7
+ const typeNames = [];
8
+ for (const t in Types) {
9
+ typeNames[Types[t]] = t;
10
+ }
11
+
12
+ function readField({ packet, type, charset, encoding, config, options }) {
13
+ const supportBigNumbers = Boolean(
14
+ options.supportBigNumbers || config.supportBigNumbers,
15
+ );
16
+ const bigNumberStrings = Boolean(
17
+ options.bigNumberStrings || config.bigNumberStrings,
18
+ );
19
+ const timezone = options.timezone || config.timezone;
20
+ const dateStrings = options.dateStrings || config.dateStrings;
21
+
22
+ switch (type) {
23
+ case Types.TINY:
24
+ case Types.SHORT:
25
+ case Types.LONG:
26
+ case Types.INT24:
27
+ case Types.YEAR:
28
+ return packet.parseLengthCodedIntNoBigCheck();
29
+ case Types.LONGLONG:
30
+ if (supportBigNumbers && bigNumberStrings) {
31
+ return packet.parseLengthCodedIntString();
32
+ }
33
+ return packet.parseLengthCodedInt(supportBigNumbers);
34
+ case Types.FLOAT:
35
+ case Types.DOUBLE:
36
+ return packet.parseLengthCodedFloat();
37
+ case Types.NULL:
38
+ case Types.DECIMAL:
39
+ case Types.NEWDECIMAL:
40
+ if (config.decimalNumbers) {
41
+ return packet.parseLengthCodedFloat();
42
+ }
43
+ return packet.readLengthCodedString('ascii');
44
+ case Types.DATE:
45
+ if (helpers.typeMatch(type, dateStrings, Types)) {
46
+ return packet.readLengthCodedString('ascii');
47
+ }
48
+ return packet.parseDate(timezone);
49
+ case Types.DATETIME:
50
+ case Types.TIMESTAMP:
51
+ if (helpers.typeMatch(type, dateStrings, Types)) {
52
+ return packet.readLengthCodedString('ascii');
53
+ }
54
+ return packet.parseDateTime(timezone);
55
+ case Types.TIME:
56
+ return packet.readLengthCodedString('ascii');
57
+ case Types.GEOMETRY:
58
+ return packet.parseGeometryValue();
59
+ case Types.JSON:
60
+ // Since for JSON columns mysql always returns charset 63 (BINARY),
61
+ // we have to handle it according to JSON specs and use "utf8",
62
+ // see https://github.com/sidorares/node-mysql2/issues/409
63
+ return config.jsonStrings
64
+ ? packet.readLengthCodedString('utf8')
65
+ : JSON.parse(packet.readLengthCodedString('utf8'));
66
+ default:
67
+ if (charset === Charsets.BINARY) {
68
+ return packet.readLengthCodedBuffer();
69
+ }
70
+ return packet.readLengthCodedString(encoding);
71
+ }
72
+ }
73
+
74
+ function createTypecastField(field, packet) {
75
+ return {
76
+ type: typeNames[field.columnType],
77
+ length: field.columnLength,
78
+ db: field.schema,
79
+ table: field.table,
80
+ name: field.name,
81
+ string: function (encoding = field.encoding) {
82
+ if (field.columnType === Types.JSON && encoding === field.encoding) {
83
+ // Since for JSON columns mysql always returns charset 63 (BINARY),
84
+ // we have to handle it according to JSON specs and use "utf8",
85
+ // see https://github.com/sidorares/node-mysql2/issues/1661
86
+ console.warn(
87
+ `typeCast: JSON column "${field.name}" is interpreted as BINARY by default, recommended to manually set utf8 encoding: \`field.string("utf8")\``,
88
+ );
89
+ }
90
+ return packet.readLengthCodedString(encoding);
91
+ },
92
+ buffer: function () {
93
+ return packet.readLengthCodedBuffer();
94
+ },
95
+ geometry: function () {
96
+ return packet.parseGeometryValue();
97
+ },
98
+ };
99
+ }
100
+
101
+ function getTextParser(_fields, _options, config) {
102
+ return {
103
+ next(packet, fields, options) {
104
+ const result = options.rowsAsArray ? [] : {};
105
+ for (let i = 0; i < fields.length; i++) {
106
+ const field = fields[i];
107
+ const typeCast = options.typeCast ? options.typeCast : config.typeCast;
108
+ const next = () =>
109
+ readField({
110
+ packet,
111
+ type: field.columnType,
112
+ encoding: field.encoding,
113
+ charset: field.characterSet,
114
+ config,
115
+ options,
116
+ });
117
+
118
+ let value;
119
+
120
+ if (options.typeCast === false) {
121
+ value = packet.readLengthCodedBuffer();
122
+ } else if (typeof typeCast === 'function') {
123
+ value = typeCast(createTypecastField(field, packet), next);
124
+ } else {
125
+ value = next();
126
+ }
127
+
128
+ if (options.rowsAsArray) {
129
+ result.push(value);
130
+ } else if (typeof options.nestTables === 'string') {
131
+ result[
132
+ `${helpers.fieldEscape(field.table, false)}${options.nestTables}${helpers.fieldEscape(field.name, false)}`
133
+ ] = value;
134
+ } else if (options.nestTables) {
135
+ const tableName = helpers.fieldEscape(field.table, false);
136
+ if (!result[tableName]) {
137
+ result[tableName] = {};
138
+ }
139
+ result[tableName][helpers.fieldEscape(field.name, false)] = value;
140
+ } else {
141
+ result[helpers.fieldEscape(field.name, false)] = value;
142
+ }
143
+ }
144
+
145
+ return result;
146
+ },
147
+ };
148
+ }
149
+
150
+ module.exports = getTextParser;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mysql2",
3
- "version": "3.12.0",
3
+ "version": "3.12.1-canary.51da6534",
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",
@@ -8,12 +8,13 @@
8
8
  "scripts": {
9
9
  "lint": "npm run lint:docs && npm run lint:code",
10
10
  "lint:code": "eslint index.js promise.js index.d.ts promise.d.ts \"typings/**/*.ts\" \"lib/**/*.js\" \"test/**/*.{js,cjs,mjs,ts}\" \"benchmarks/**/*.js\"",
11
+ "lint:fix": "npm run lint:docs -- --fix &&npm run lint:code -- --fix",
11
12
  "lint:docs": "eslint Contributing.md README.md",
12
13
  "lint:typings": "npx prettier --check ./typings",
13
14
  "lint:tests": "npx prettier --check ./test",
14
- "test": "poku -d test/esm test/unit test/integration",
15
- "test:bun": "poku -d --bun test/esm test/unit test/integration",
16
- "test:deno": "deno run --allow-read --allow-env --allow-run npm:poku -d --deno --denoAllow=\"read,env,net,sys\" test/esm test/unit test/integration",
15
+ "test": "poku -d -r=verbose --sequential test/esm test/unit test/integration",
16
+ "test:bun": "bun poku -d --sequential test/esm test/unit test/integration",
17
+ "test:deno": "deno run --allow-read --allow-env --allow-run npm:poku -d --sequential --denoAllow=\"read,env,net,sys\" test/esm test/unit test/integration",
17
18
  "test:tsc-build": "cd \"test/tsc-build\" && npx tsc -p \"tsconfig.json\"",
18
19
  "coverage-test": "c8 npm run test",
19
20
  "benchmark": "node ./benchmarks/benchmark.js",
@@ -78,11 +79,11 @@
78
79
  "c8": "^10.1.1",
79
80
  "error-stack-parser": "^2.0.3",
80
81
  "eslint": "^8.27.0",
81
- "eslint-config-prettier": "^9.0.0",
82
+ "eslint-config-prettier": "^10.0.1",
82
83
  "eslint-plugin-async-await": "0.0.0",
83
84
  "eslint-plugin-markdown": "^5.0.0",
84
85
  "lint-staged": "^15.0.1",
85
- "poku": "^2.0.0",
86
+ "poku": "^3.0.0",
86
87
  "portfinder": "^1.0.28",
87
88
  "prettier": "^3.0.0",
88
89
  "progress": "^2.0.3",
@@ -323,6 +323,8 @@ export interface ConnectionOptions {
323
323
 
324
324
  waitForConnections?: boolean;
325
325
 
326
+ disableEval?: boolean;
327
+
326
328
  authPlugins?: {
327
329
  [key: string]: AuthPlugin;
328
330
  };