mysql2 3.22.6 → 3.22.7-canary.5034e57

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.
@@ -87,6 +87,8 @@ class BaseConnection extends EventEmitter {
87
87
  },
88
88
  });
89
89
  this.serverCapabilityFlags = 0;
90
+ this._isMariaDB = false;
91
+ this._mariadbExtendedMetadata = false;
90
92
  this.authorized = false;
91
93
  this.sequenceId = 0;
92
94
  this.compressedSequenceId = 0;
@@ -13,6 +13,7 @@
13
13
  const Command = require('./command.js');
14
14
  const Packets = require('../packets/index.js');
15
15
  const ClientConstants = require('../constants/client.js');
16
+ const MariaDBClientConstants = require('../constants/mariadb_client.js');
16
17
  const CharsetToEncoding = require('../constants/charset_encodings.js');
17
18
  const auth41 = require('../auth_41.js');
18
19
  const { getAuthPlugin } = require('./auth_switch.js');
@@ -35,6 +36,7 @@ class ClientHandshake extends Command {
35
36
  super();
36
37
  this.handshake = null;
37
38
  this.clientFlags = clientFlags;
39
+ this.mariadbExtendedClientFlags = 0;
38
40
  this.authenticationFactor = 0;
39
41
  }
40
42
 
@@ -45,7 +47,8 @@ class ClientHandshake extends Command {
45
47
  sendSSLRequest(connection) {
46
48
  const sslRequest = new Packets.SSLRequest(
47
49
  this.clientFlags,
48
- connection.config.charsetNumber
50
+ connection.config.charsetNumber,
51
+ this.mariadbExtendedClientFlags
49
52
  );
50
53
  connection.writePacket(sslRequest.toPacket());
51
54
  }
@@ -130,6 +133,7 @@ class ClientHandshake extends Command {
130
133
 
131
134
  const handshakeResponse = new Packets.HandshakeResponse({
132
135
  flags: this.clientFlags,
136
+ mariadbExtendedClientFlags: this.mariadbExtendedClientFlags,
133
137
  user: this.user,
134
138
  database: this.database,
135
139
  password: this.password,
@@ -265,6 +269,26 @@ class ClientHandshake extends Command {
265
269
  connection.serverCapabilityFlags = this.handshake.capabilityFlags;
266
270
  connection.serverEncoding = CharsetToEncoding[this.handshake.characterSet];
267
271
  connection.connectionId = this.handshake.connectionId;
272
+ // MariaDB needs a few protocol deviations, e.g. it rejects the JSON
273
+ // parameter type in COM_STMT_EXECUTE
274
+ connection._isMariaDB = /mariadb/i.test(this.handshake.serverVersion);
275
+ // If a MariaDB server offers extended column metadata, request it so
276
+ // MariaDB-specific column types (UUID, INET4, INET6, VECTOR and the JSON
277
+ // alias) can be identified. The server only reads the extended client
278
+ // capability bytes when the client leaves CLIENT_MYSQL (LONG_PASSWORD)
279
+ // unset, mirroring the server side of the negotiation.
280
+ if (
281
+ this.handshake.mariadbExtendedCapabilityFlags &
282
+ MariaDBClientConstants.MARIADB_CLIENT_EXTENDED_METADATA
283
+ ) {
284
+ this.clientFlags &= ~ClientConstants.LONG_PASSWORD;
285
+ this.mariadbExtendedClientFlags |=
286
+ MariaDBClientConstants.MARIADB_CLIENT_EXTENDED_METADATA;
287
+ }
288
+ connection._mariadbExtendedMetadata = Boolean(
289
+ this.mariadbExtendedClientFlags &
290
+ MariaDBClientConstants.MARIADB_CLIENT_EXTENDED_METADATA
291
+ );
268
292
  const serverSSLSupport =
269
293
  this.handshake.capabilityFlags & ClientConstants.SSL;
270
294
  // multi factor authentication is enabled with the
@@ -51,7 +51,8 @@ class Execute extends Command {
51
51
  connection.config.charsetNumber,
52
52
  connection.config.timezone,
53
53
  this._executeOptions.attributes,
54
- clientFlags
54
+ clientFlags,
55
+ connection._isMariaDB
55
56
  );
56
57
  //For reasons why this try-catch is here, please see
57
58
  // https://github.com/sidorares/node-mysql2/pull/689
@@ -77,7 +78,8 @@ class Execute extends Command {
77
78
  // this.statement.columns[this._receivedFieldsCount] : new Packets.ColumnDefinition(packet);
78
79
  const field = new Packets.ColumnDefinition(
79
80
  packet,
80
- connection.clientEncoding
81
+ connection.clientEncoding,
82
+ connection._mariadbExtendedMetadata
81
83
  );
82
84
  this._receivedFieldsCount++;
83
85
  this._fields[this._resultIndex].push(field);
@@ -87,7 +87,11 @@ class Prepare extends Command {
87
87
  }
88
88
  return this.prepareDone(connection);
89
89
  }
90
- const def = new Packets.ColumnDefinition(packet, connection.clientEncoding);
90
+ const def = new Packets.ColumnDefinition(
91
+ packet,
92
+ connection.clientEncoding,
93
+ connection._mariadbExtendedMetadata
94
+ );
91
95
  this.parameterDefinitions.push(def);
92
96
  if (this.parameterDefinitions.length === this.parameterCount) {
93
97
  return Prepare.prototype.parametersEOF;
@@ -99,7 +103,11 @@ class Prepare extends Command {
99
103
  if (packet.isEOF()) {
100
104
  return this.prepareDone(connection);
101
105
  }
102
- const def = new Packets.ColumnDefinition(packet, connection.clientEncoding);
106
+ const def = new Packets.ColumnDefinition(
107
+ packet,
108
+ connection.clientEncoding,
109
+ connection._mariadbExtendedMetadata
110
+ );
103
111
  this.fields.push(def);
104
112
  if (this.fields.length === this.fieldCount) {
105
113
  return Prepare.prototype.fieldsEOF;
@@ -199,7 +199,8 @@ class Query extends Command {
199
199
  if (this._fields[this._resultIndex].length !== this._fieldCount) {
200
200
  const field = new Packets.ColumnDefinition(
201
201
  packet,
202
- connection.clientEncoding
202
+ connection.clientEncoding,
203
+ connection._mariadbExtendedMetadata
203
204
  );
204
205
  this._fields[this._resultIndex].push(field);
205
206
  if (connection.config.debug) {
@@ -3,7 +3,7 @@
3
3
  // see tools/generate-charset-mapping.js
4
4
  // basicalliy result of "SHOW COLLATION" query
5
5
 
6
- module.exports = [
6
+ const encodings = [
7
7
  'utf8',
8
8
  'big5',
9
9
  'latin2',
@@ -315,3 +315,27 @@ module.exports = [
315
315
  'utf8',
316
316
  'utf8',
317
317
  ];
318
+
319
+ // MariaDB NO PAD collations mirror their PAD SPACE counterparts:
320
+ // collation id = PAD SPACE collation id + 1024
321
+ const padSpaceLength = encodings.length;
322
+ for (let id = 1025; id < 1024 + padSpaceLength; id++) {
323
+ encodings[id] = encodings[id - 1024];
324
+ }
325
+
326
+ // MariaDB UCA-14.0.0 (uca1400) collations use fixed 256-id blocks
327
+ // per character set, each covering the PAD SPACE and NO PAD variants
328
+ const uca1400Blocks = [
329
+ [2048, 'cesu8'], // utf8mb3
330
+ [2304, 'utf8'], // utf8mb4
331
+ [2560, 'ucs2'], // ucs2
332
+ [2816, 'utf16'], // utf16
333
+ [3072, 'utf32'], // utf32
334
+ ];
335
+ for (const [start, encoding] of uca1400Blocks) {
336
+ for (let id = start; id < start + 256; id++) {
337
+ encodings[id] = encoding;
338
+ }
339
+ }
340
+
341
+ module.exports = encodings;
@@ -0,0 +1,19 @@
1
+ 'use strict';
2
+
3
+ // MariaDB-specific extended capability flags.
4
+ //
5
+ // MariaDB servers (10.2+) do not set the CLIENT_MYSQL (LONG_PASSWORD)
6
+ // capability and instead use 4 reserved bytes of the initial handshake packet
7
+ // to advertise these flags. Clients answer with their own extended
8
+ // capabilities in the last 4 reserved bytes of the handshake response
9
+ // (the server only reads them when the client leaves CLIENT_MYSQL unset).
10
+ //
11
+ // The flags below are the lower 32 bits shifted down, i.e.
12
+ // MARIADB_CLIENT_PROGRESS is documented as 1 << 32.
13
+ // https://mariadb.com/docs/server/reference/clientserver-protocol/1-connecting/connection
14
+ exports.MARIADB_CLIENT_PROGRESS = 0x00000001;
15
+ exports.MARIADB_CLIENT_COM_MULTI = 0x00000002; /* deprecated */
16
+ exports.MARIADB_CLIENT_STMT_BULK_OPERATIONS = 0x00000004;
17
+ exports.MARIADB_CLIENT_EXTENDED_METADATA = 0x00000008;
18
+ exports.MARIADB_CLIENT_CACHE_METADATA = 0x00000010;
19
+ exports.MARIADB_CLIENT_BULK_UNIT_RESULTS = 0x00000020;
@@ -19,7 +19,7 @@ const fields = ['catalog', 'schema', 'table', 'orgTable', 'name', 'orgName'];
19
19
  // see https://github.com/sidorares/node-mysql2/pull/137
20
20
  //
21
21
  class ColumnDefinition {
22
- constructor(packet, clientEncoding) {
22
+ constructor(packet, clientEncoding, mariadbExtendedMetadata) {
23
23
  this._buf = packet.buffer;
24
24
  this._clientEncoding = clientEncoding;
25
25
  this._catalogLength = packet.readLengthCodedNumber();
@@ -41,6 +41,14 @@ class ColumnDefinition {
41
41
  this._orgNameLength = packet.readLengthCodedNumber();
42
42
  this._orgNameStart = packet.offset;
43
43
  packet.offset += this._orgNameLength;
44
+ // MariaDB-specific column types (UUID, INET4, INET6, VECTOR and the JSON
45
+ // alias) are sent as standard string/blob types on the wire; the real
46
+ // type is only visible here, in the extended metadata block negotiated
47
+ // via the MARIADB_CLIENT_EXTENDED_METADATA capability (parsed out of
48
+ // line and with prototype defaults to keep this hot constructor small)
49
+ if (mariadbExtendedMetadata) {
50
+ this._parseMariadbExtendedMetadata(packet);
51
+ }
44
52
  packet.skip(1); // length of the following fields (always 0x0c)
45
53
  this.characterSet = packet.readInt16();
46
54
  this.encoding = CharsetToEncoding[this.characterSet];
@@ -57,6 +65,27 @@ class ColumnDefinition {
57
65
  this.decimals = packet.readInt8();
58
66
  }
59
67
 
68
+ _parseMariadbExtendedMetadata(packet) {
69
+ // length-encoded block of { int<1> id, string<lenenc> value } pairs
70
+ const extendedMetadataLength = packet.readLengthCodedNumber() || 0;
71
+ const extendedMetadataEnd = Math.min(
72
+ packet.offset + extendedMetadataLength,
73
+ packet.end
74
+ );
75
+ while (packet.offset < extendedMetadataEnd) {
76
+ const id = packet.readInt8();
77
+ const value = packet.readLengthCodedString('ascii');
78
+ if (id === 0) {
79
+ this.extendedTypeName = value; // e.g. 'uuid', 'inet4', 'inet6', 'point'
80
+ } else if (id === 1) {
81
+ this.extendedFormat = value; // e.g. 'json'
82
+ }
83
+ }
84
+ // a malformed entry could read past the block; resynchronise so the
85
+ // fixed column definition fields that follow parse from the right offset
86
+ packet.offset = extendedMetadataEnd;
87
+ }
88
+
60
89
  inspect() {
61
90
  return {
62
91
  catalog: this.catalog,
@@ -288,4 +317,9 @@ addString('table');
288
317
  addString('orgTable');
289
318
  addString('orgName');
290
319
 
320
+ // MariaDB extended metadata defaults; instances only carry own values when
321
+ // the server actually tagged the column
322
+ ColumnDefinition.prototype.extendedTypeName = undefined;
323
+ ColumnDefinition.prototype.extendedFormat = undefined;
324
+
291
325
  module.exports = ColumnDefinition;
@@ -11,7 +11,7 @@ function isJSON(value) {
11
11
  );
12
12
  }
13
13
 
14
- function toParameter(value, encoding, timezone) {
14
+ function toParameter(value, encoding, timezone, jsonAsString) {
15
15
  let type = Types.VAR_STRING;
16
16
  let length;
17
17
  let writer = function (value) {
@@ -46,8 +46,18 @@ function toParameter(value, encoding, timezone) {
46
46
  };
47
47
  } else if (isJSON(value)) {
48
48
  value = JSON.stringify(value);
49
- type = Types.JSON;
49
+ // MariaDB rejects the JSON parameter type with "Incorrect
50
+ // arguments to mysqld_stmt_execute"; it expects JSON values
51
+ // as plain strings
52
+ if (!jsonAsString) {
53
+ type = Types.JSON;
54
+ }
50
55
  } else if (Buffer.isBuffer(value)) {
56
+ // send buffers as BLOB so servers treat the value as binary data
57
+ // rather than a string in the connection charset (MariaDB converts
58
+ // string parameters when storing into binary columns such as
59
+ // VECTOR, corrupting the value)
60
+ type = Types.BLOB;
51
61
  length = Packet.lengthCodedNumberLength(value.length) + value.length;
52
62
  writer = Packet.prototype.writeLengthCodedBuffer;
53
63
  }
@@ -15,7 +15,8 @@ class Execute {
15
15
  charsetNumber,
16
16
  timezone,
17
17
  attributes,
18
- clientFlags
18
+ clientFlags,
19
+ jsonAsString
19
20
  ) {
20
21
  this.id = id;
21
22
  this.parameters = parameters;
@@ -23,6 +24,7 @@ class Execute {
23
24
  this.timezone = timezone;
24
25
  this.attributes = attributes;
25
26
  this.clientFlags = clientFlags || 0;
27
+ this.jsonAsString = jsonAsString || false;
26
28
  }
27
29
 
28
30
  static fromPacket(packet, encoding) {
@@ -34,6 +36,7 @@ class Execute {
34
36
  while (i < packet.end - 1) {
35
37
  if (
36
38
  (packet.buffer[i + 1] === Types.VAR_STRING ||
39
+ packet.buffer[i + 1] === Types.BLOB ||
37
40
  packet.buffer[i + 1] === Types.NULL ||
38
41
  packet.buffer[i + 1] === Types.DOUBLE ||
39
42
  packet.buffer[i + 1] === Types.TINY ||
@@ -54,6 +57,7 @@ class Execute {
54
57
  for (let i = packet.offset + 1; i < packet.end - 1; i++) {
55
58
  if (
56
59
  (packet.buffer[i] === Types.VAR_STRING ||
60
+ packet.buffer[i] === Types.BLOB ||
57
61
  packet.buffer[i] === Types.NULL ||
58
62
  packet.buffer[i] === Types.DOUBLE ||
59
63
  packet.buffer[i] === Types.TINY ||
@@ -72,6 +76,8 @@ class Execute {
72
76
  for (let i = 0; i < types.length; i++) {
73
77
  if (types[i] === Types.VAR_STRING) {
74
78
  values.push(packet.readLengthCodedString(encoding));
79
+ } else if (types[i] === Types.BLOB) {
80
+ values.push(packet.readLengthCodedBuffer());
75
81
  } else if (types[i] === Types.DOUBLE) {
76
82
  values.push(packet.readDouble());
77
83
  } else if (types[i] === Types.TINY) {
@@ -119,7 +125,7 @@ class Execute {
119
125
  const bindParams =
120
126
  numParams > 0
121
127
  ? this.parameters.map((v) =>
122
- toParameter(v, this.encoding, this.timezone)
128
+ toParameter(v, this.encoding, this.timezone, this.jsonAsString)
123
129
  )
124
130
  : [];
125
131
  const attrParams = attrNames.map((name) =>
@@ -16,6 +16,7 @@ class Handshake {
16
16
  this.characterSet = args.characterSet;
17
17
  this.statusFlags = args.statusFlags;
18
18
  this.authPluginName = args.authPluginName;
19
+ this.mariadbExtendedCapabilityFlags = args.mariadbExtendedCapabilityFlags;
19
20
  }
20
21
 
21
22
  setScrambleData(cb) {
@@ -79,9 +80,20 @@ class Handshake {
79
80
  args.authPluginDataLength = 0;
80
81
  packet.skip(1);
81
82
  }
82
- packet.skip(10);
83
+ packet.skip(6);
84
+ if (args.capabilityFlags & ClientConstants.LONG_PASSWORD) {
85
+ // CLIENT_MYSQL (LONG_PASSWORD) set: a MySQL server, the remaining
86
+ // 4 reserved bytes are filler
87
+ packet.skip(4);
88
+ args.mariadbExtendedCapabilityFlags = 0;
89
+ } else {
90
+ // MariaDB servers (10.2+) leave CLIENT_MYSQL unset and use these
91
+ // 4 bytes to advertise MariaDB-specific capabilities
92
+ args.mariadbExtendedCapabilityFlags = packet.readInt32();
93
+ }
83
94
  } else {
84
95
  args.capabilityFlags = capabilityFlagsBuffer.readUInt16LE(0);
96
+ args.mariadbExtendedCapabilityFlags = 0;
85
97
  }
86
98
 
87
99
  const isSecureConnection =
@@ -16,6 +16,7 @@ class HandshakeResponse {
16
16
  this.authPluginData2 = handshake.authPluginData2;
17
17
  this.compress = handshake.compress;
18
18
  this.clientFlags = handshake.flags;
19
+ this.mariadbExtendedClientFlags = handshake.mariadbExtendedClientFlags || 0;
19
20
 
20
21
  // Accept pre-calculated authToken and authPluginName from caller
21
22
  // This allows the caller to optimize by using the server's preferred auth method
@@ -69,7 +70,10 @@ class HandshakeResponse {
69
70
  packet.writeInt32(this.clientFlags);
70
71
  packet.writeInt32(0); // max packet size. todo: move to config
71
72
  packet.writeInt8(this.charsetNumber);
72
- packet.skip(23);
73
+ // the last 4 of the 23 reserved bytes carry the MariaDB extended client
74
+ // capabilities (zero when not negotiated, i.e. plain filler)
75
+ packet.skip(19);
76
+ packet.writeInt32(this.mariadbExtendedClientFlags);
73
77
  const encoding = this.encoding;
74
78
  packet.writeNullTerminatedString(this.user, encoding);
75
79
  let k;
@@ -4,9 +4,10 @@ const ClientConstants = require('../constants/client');
4
4
  const Packet = require('../packets/packet');
5
5
 
6
6
  class SSLRequest {
7
- constructor(flags, charset) {
7
+ constructor(flags, charset, mariadbExtendedClientFlags) {
8
8
  this.clientFlags = flags | ClientConstants.SSL;
9
9
  this.charset = charset;
10
+ this.mariadbExtendedClientFlags = mariadbExtendedClientFlags || 0;
10
11
  }
11
12
 
12
13
  toPacket() {
@@ -18,6 +19,10 @@ class SSLRequest {
18
19
  packet.writeInt32(this.clientFlags);
19
20
  packet.writeInt32(0); // max packet size. todo: move to config
20
21
  packet.writeInt8(this.charset);
22
+ // the last 4 of the 23 reserved bytes carry the MariaDB extended client
23
+ // capabilities (zero when not negotiated, i.e. plain filler)
24
+ packet.skip(19);
25
+ packet.writeInt32(this.mariadbExtendedClientFlags);
21
26
  return packet;
22
27
  }
23
28
  }
@@ -21,6 +21,13 @@ function readCodeFor(field, config, options, fieldNum) {
21
21
  const timezone = options.timezone || config.timezone;
22
22
  const dateStrings = options.dateStrings || config.dateStrings;
23
23
  const unsigned = field.flags & FieldFlags.UNSIGNED;
24
+ // MariaDB sends JSON values as LONGTEXT; the JSON type is only visible in
25
+ // the extended metadata (MARIADB_CLIENT_EXTENDED_METADATA capability)
26
+ if (field.extendedFormat === 'json') {
27
+ return config.jsonStrings
28
+ ? `packet.readLengthCodedString(fields[${fieldNum}].encoding)`
29
+ : `JSON.parse(packet.readLengthCodedString(fields[${fieldNum}].encoding));`;
30
+ }
24
31
  switch (field.columnType) {
25
32
  case Types.TINY:
26
33
  return unsigned ? 'packet.readInt8();' : 'packet.readSInt8();';
@@ -92,6 +99,8 @@ function compile(fields, options, config) {
92
99
  function wrap(field, packet) {
93
100
  return {
94
101
  type: typeNames[field.columnType],
102
+ extendedTypeName: field.extendedTypeName,
103
+ extendedFormat: field.extendedFormat,
95
104
  length: field.columnLength,
96
105
  db: field.schema,
97
106
  table: field.table,
@@ -20,6 +20,7 @@ function keyFromFields(type, fields, options, config) {
20
20
  options.timezone || config.timezone,
21
21
  Boolean(options.decimalNumbers),
22
22
  options.dateStrings,
23
+ Boolean(config.jsonStrings),
23
24
  ];
24
25
 
25
26
  for (let i = 0; i < fields.length; ++i) {
@@ -33,6 +34,8 @@ function keyFromFields(type, fields, options, config) {
33
34
  field.table,
34
35
  field.flags,
35
36
  field.characterSet,
37
+ field.extendedTypeName,
38
+ field.extendedFormat,
36
39
  ]);
37
40
  }
38
41
 
@@ -22,6 +22,14 @@ function getBinaryParser(fields, _options, config) {
22
22
  const dateStrings = options.dateStrings || config.dateStrings;
23
23
  const unsigned = field.flags & FieldFlags.UNSIGNED;
24
24
 
25
+ // MariaDB sends JSON values as LONGTEXT; the JSON type is only visible in
26
+ // the extended metadata (MARIADB_CLIENT_EXTENDED_METADATA capability)
27
+ if (field.extendedFormat === 'json') {
28
+ return config.jsonStrings
29
+ ? packet.readLengthCodedString(field.encoding)
30
+ : JSON.parse(packet.readLengthCodedString(field.encoding));
31
+ }
32
+
25
33
  switch (field.columnType) {
26
34
  case Types.TINY:
27
35
  return unsigned ? packet.readInt8() : packet.readSInt8();
@@ -120,6 +128,8 @@ function getBinaryParser(fields, _options, config) {
120
128
  ? typeCast(
121
129
  {
122
130
  type: typeNames[field.columnType],
131
+ extendedTypeName: field.extendedTypeName,
132
+ extendedFormat: field.extendedFormat,
123
133
  length: field.columnLength,
124
134
  db: field.schema,
125
135
  table: field.table,
@@ -9,7 +9,15 @@ for (const t in Types) {
9
9
  typeNames[Types[t]] = t;
10
10
  }
11
11
 
12
- function readField({ packet, type, charset, encoding, config, options }) {
12
+ function readField({
13
+ packet,
14
+ field,
15
+ type,
16
+ charset,
17
+ encoding,
18
+ config,
19
+ options,
20
+ }) {
13
21
  const supportBigNumbers = Boolean(
14
22
  options.supportBigNumbers || config.supportBigNumbers
15
23
  );
@@ -19,6 +27,14 @@ function readField({ packet, type, charset, encoding, config, options }) {
19
27
  const timezone = options.timezone || config.timezone;
20
28
  const dateStrings = options.dateStrings || config.dateStrings;
21
29
 
30
+ // MariaDB sends JSON values as LONGTEXT; the JSON type is only visible in
31
+ // the extended metadata (MARIADB_CLIENT_EXTENDED_METADATA capability)
32
+ if (field.extendedFormat === 'json') {
33
+ return config.jsonStrings
34
+ ? packet.readLengthCodedString(encoding)
35
+ : JSON.parse(packet.readLengthCodedString(encoding));
36
+ }
37
+
22
38
  switch (type) {
23
39
  case Types.TINY:
24
40
  case Types.SHORT:
@@ -76,6 +92,8 @@ function readField({ packet, type, charset, encoding, config, options }) {
76
92
  function createTypecastField(field, packet) {
77
93
  return {
78
94
  type: typeNames[field.columnType],
95
+ extendedTypeName: field.extendedTypeName,
96
+ extendedFormat: field.extendedFormat,
79
97
  length: field.columnLength,
80
98
  db: field.schema,
81
99
  table: field.table,
@@ -110,6 +128,7 @@ function getTextParser(_fields, _options, config) {
110
128
  const next = () =>
111
129
  readField({
112
130
  packet,
131
+ field,
113
132
  type: field.columnType,
114
133
  encoding: field.encoding,
115
134
  charset: field.characterSet,
@@ -11,7 +11,9 @@ for (const t in Types) {
11
11
  typeNames[Types[t]] = t;
12
12
  }
13
13
 
14
- function readCodeFor(type, charset, encodingExpr, config, options) {
14
+ function readCodeFor(field, encodingExpr, config, options) {
15
+ const type = field.columnType;
16
+ const charset = field.characterSet;
15
17
  const supportBigNumbers = Boolean(
16
18
  options.supportBigNumbers || config.supportBigNumbers
17
19
  );
@@ -21,6 +23,14 @@ function readCodeFor(type, charset, encodingExpr, config, options) {
21
23
  const timezone = options.timezone || config.timezone;
22
24
  const dateStrings = options.dateStrings || config.dateStrings;
23
25
 
26
+ // MariaDB sends JSON values as LONGTEXT; the JSON type is only visible in
27
+ // the extended metadata (MARIADB_CLIENT_EXTENDED_METADATA capability)
28
+ if (field.extendedFormat === 'json') {
29
+ return config.jsonStrings
30
+ ? `packet.readLengthCodedString(${encodingExpr})`
31
+ : `JSON.parse(packet.readLengthCodedString(${encodingExpr}))`;
32
+ }
33
+
24
34
  switch (type) {
25
35
  case Types.TINY:
26
36
  case Types.SHORT:
@@ -88,6 +98,8 @@ function compile(fields, options, config) {
88
98
  function wrap(field, _this) {
89
99
  return {
90
100
  type: typeNames[field.columnType],
101
+ extendedTypeName: field.extendedTypeName,
102
+ extendedFormat: field.extendedFormat,
91
103
  length: field.columnLength,
92
104
  db: field.schema,
93
105
  table: field.table,
@@ -174,13 +186,7 @@ function compile(fields, options, config) {
174
186
  parserFn(`${lvalue} = packet.readLengthCodedBuffer();`);
175
187
  } else {
176
188
  const encodingExpr = `fields[${i}].encoding`;
177
- const readCode = readCodeFor(
178
- fields[i].columnType,
179
- fields[i].characterSet,
180
- encodingExpr,
181
- config,
182
- options
183
- );
189
+ const readCode = readCodeFor(fields[i], encodingExpr, config, options);
184
190
  if (typeof options.typeCast === 'function') {
185
191
  parserFn(
186
192
  `${lvalue} = options.typeCast(this.wrap${i}, function() { return ${readCode} });`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mysql2",
3
- "version": "3.22.6",
3
+ "version": "3.22.7-canary.5034e57",
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",
@@ -40,6 +40,17 @@ export type Type = {
40
40
  };
41
41
 
42
42
  export type Field = Type & {
43
+ /**
44
+ * MariaDB extended data type name (e.g. `uuid`, `inet4`, `inet6`, `point`),
45
+ * available when the server supports the MARIADB_CLIENT_EXTENDED_METADATA
46
+ * capability (MariaDB 10.5+)
47
+ */
48
+ extendedTypeName?: string;
49
+ /**
50
+ * MariaDB extended format name (e.g. `json`), available when the server
51
+ * supports the MARIADB_CLIENT_EXTENDED_METADATA capability (MariaDB 10.5+)
52
+ */
53
+ extendedFormat?: string;
43
54
  length: number;
44
55
  db: string;
45
56
  table: string;
@@ -22,6 +22,17 @@ declare interface FieldPacket {
22
22
  typeName?: string;
23
23
  encoding?: string;
24
24
  columnLength?: number;
25
+ /**
26
+ * MariaDB extended data type name (e.g. `uuid`, `inet4`, `inet6`, `point`),
27
+ * available when the server supports the MARIADB_CLIENT_EXTENDED_METADATA
28
+ * capability (MariaDB 10.5+)
29
+ */
30
+ extendedTypeName?: string;
31
+ /**
32
+ * MariaDB extended format name (e.g. `json`), available when the server
33
+ * supports the MARIADB_CLIENT_EXTENDED_METADATA capability (MariaDB 10.5+)
34
+ */
35
+ extendedFormat?: string;
25
36
  }
26
37
 
27
38
  export { FieldPacket };