@depup/mysql2 3.22.0-depup.0 → 3.23.2-depup.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.
Files changed (42) hide show
  1. package/README.md +9 -3
  2. package/changes.json +8 -3
  3. package/lib/base/connection.js +4 -2
  4. package/lib/base/pool.js +4 -0
  5. package/lib/commands/client_handshake.js +26 -2
  6. package/lib/commands/execute.js +17 -4
  7. package/lib/commands/ping.js +1 -1
  8. package/lib/commands/prepare.js +10 -2
  9. package/lib/commands/query.js +2 -1
  10. package/lib/compressed_protocol.js +1 -1
  11. package/lib/connection_config.js +1 -0
  12. package/lib/constants/charset_encodings.js +25 -1
  13. package/lib/constants/mariadb_client.js +19 -0
  14. package/lib/packets/binary_row.js +2 -2
  15. package/lib/packets/binlog_query_statusvars.js +1 -1
  16. package/lib/packets/change_user.js +1 -1
  17. package/lib/packets/column_definition.js +35 -1
  18. package/lib/packets/encode_parameter.js +12 -2
  19. package/lib/packets/execute.js +8 -2
  20. package/lib/packets/handshake.js +13 -1
  21. package/lib/packets/handshake_response.js +5 -1
  22. package/lib/packets/packet.js +51 -3
  23. package/lib/packets/resultset_header.js +1 -1
  24. package/lib/packets/ssl_request.js +6 -1
  25. package/lib/packets/text_row.js +1 -1
  26. package/lib/parsers/binary_parser.js +45 -6
  27. package/lib/parsers/parser_cache.js +3 -0
  28. package/lib/parsers/static_binary_parser.js +37 -2
  29. package/lib/parsers/static_text_parser.js +21 -2
  30. package/lib/parsers/text_parser.js +15 -9
  31. package/lib/promise/capture_local_err.js +25 -0
  32. package/lib/promise/connection.js +35 -40
  33. package/lib/promise/make_done_cb.js +5 -9
  34. package/lib/promise/pool.js +11 -9
  35. package/lib/promise/pool_cluster.js +9 -2
  36. package/lib/promise/prepared_statement_info.js +5 -1
  37. package/package.json +13 -8
  38. package/promise.js +16 -15
  39. package/typings/mysql/lib/Connection.d.ts +7 -1
  40. package/typings/mysql/lib/parsers/typeCast.d.ts +11 -0
  41. package/typings/mysql/lib/protocol/packets/FieldPacket.d.ts +11 -0
  42. package/typings/mysql/lib/protocol/sequences/Query.d.ts +7 -1
@@ -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
  }
@@ -17,7 +17,7 @@ class TextRow {
17
17
  }
18
18
 
19
19
  static toPacket(columns, encoding) {
20
- const sequenceId = 0; // TODO remove, this is calculated now in connecton
20
+ const sequenceId = 0; // TODO remove, this is calculated now in connection
21
21
  let length = 0;
22
22
  columns.forEach((val) => {
23
23
  if (val === null || typeof val === 'undefined') {
@@ -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
+ : `packet.parseJson(fields[${fieldNum}].encoding, ${supportBigNumbers});`;
30
+ }
24
31
  switch (field.columnType) {
25
32
  case Types.TINY:
26
33
  return unsigned ? 'packet.readInt8();' : 'packet.readSInt8();';
@@ -63,7 +70,7 @@ function readCodeFor(field, config, options, fieldNum) {
63
70
  // see https://github.com/sidorares/node-mysql2/issues/409
64
71
  return config.jsonStrings
65
72
  ? 'packet.readLengthCodedString("utf8")'
66
- : 'JSON.parse(packet.readLengthCodedString("utf8"));';
73
+ : `packet.parseJson("utf8", ${supportBigNumbers});`;
67
74
  case Types.LONGLONG:
68
75
  if (!supportBigNumbers) {
69
76
  return unsigned
@@ -89,13 +96,21 @@ function compile(fields, options, config) {
89
96
  const parserFn = genFunc();
90
97
  const nullBitmapLength = Math.floor((fields.length + 7 + 2) / 8);
91
98
 
92
- function wrap(field, packet) {
99
+ function fieldMetadata(field) {
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,
98
107
  name: field.name,
108
+ };
109
+ }
110
+
111
+ function wrap(field, packet) {
112
+ return {
113
+ ...fieldMetadata(field),
99
114
  string: function (encoding = field.encoding) {
100
115
  if (field.columnType === Types.JSON && encoding === field.encoding) {
101
116
  // Since for JSON columns mysql always returns charset 63 (BINARY),
@@ -139,6 +154,22 @@ function compile(fields, options, config) {
139
154
  };
140
155
  }
141
156
 
157
+ /** A NULL value carries no bytes in the binary row packet, so the accessors return null without reading from it. */
158
+ function wrapNull(field) {
159
+ return {
160
+ ...fieldMetadata(field),
161
+ string: function () {
162
+ return null;
163
+ },
164
+ buffer: function () {
165
+ return null;
166
+ },
167
+ geometry: function () {
168
+ return null;
169
+ },
170
+ };
171
+ }
172
+
142
173
  parserFn('(function(){');
143
174
  parserFn('return class BinaryRow {');
144
175
  parserFn('constructor() {');
@@ -187,9 +218,17 @@ function compile(fields, options, config) {
187
218
  lvalue = `result[${fieldName}]`;
188
219
  }
189
220
 
190
- parserFn(`if (nullBitmaskByte${nullByteIndex} & ${currentFieldNullBit}) `);
191
- parserFn(`${lvalue} = null;`);
192
- parserFn('else {');
221
+ parserFn(`if (nullBitmaskByte${nullByteIndex} & ${currentFieldNullBit}) {`);
222
+ if (typeof options.typeCast === 'function') {
223
+ const nullWrapperVar = `nullWrapper${i}`;
224
+ parserFn(`const ${nullWrapperVar} = wrapNull(fields[${i}]);`);
225
+ parserFn(
226
+ `${lvalue} = options.typeCast(${nullWrapperVar}, function() { return null; });`
227
+ );
228
+ } else {
229
+ parserFn(`${lvalue} = null;`);
230
+ }
231
+ parserFn('} else {');
193
232
 
194
233
  if (options.typeCast === false) {
195
234
  parserFn(`${lvalue} = packet.readLengthCodedBuffer();`);
@@ -225,7 +264,7 @@ function compile(fields, options, config) {
225
264
  parserFn.toString()
226
265
  );
227
266
  }
228
- return parserFn.toFunction({ wrap });
267
+ return parserFn.toFunction({ wrap, wrapNull });
229
268
  }
230
269
 
231
270
  function getBinaryParser(fields, options, config) {
@@ -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
+ : packet.parseJson(field.encoding, supportBigNumbers);
31
+ }
32
+
25
33
  switch (field.columnType) {
26
34
  case Types.TINY:
27
35
  return unsigned ? packet.readInt8() : packet.readSInt8();
@@ -66,7 +74,7 @@ function getBinaryParser(fields, _options, config) {
66
74
  // see https://github.com/sidorares/node-mysql2/issues/409
67
75
  return config.jsonStrings
68
76
  ? packet.readLengthCodedString('utf8')
69
- : JSON.parse(packet.readLengthCodedString('utf8'));
77
+ : packet.parseJson('utf8', supportBigNumbers);
70
78
  case Types.LONGLONG:
71
79
  if (!supportBigNumbers)
72
80
  return unsigned
@@ -86,6 +94,28 @@ function getBinaryParser(fields, _options, config) {
86
94
  }
87
95
  }
88
96
 
97
+ /** A NULL value carries no bytes in the binary row packet, so the accessors return null without reading from it. */
98
+ function wrapNull(field) {
99
+ return {
100
+ type: typeNames[field.columnType],
101
+ extendedTypeName: field.extendedTypeName,
102
+ extendedFormat: field.extendedFormat,
103
+ length: field.columnLength,
104
+ db: field.schema,
105
+ table: field.table,
106
+ name: field.name,
107
+ string: function () {
108
+ return null;
109
+ },
110
+ buffer: function () {
111
+ return null;
112
+ },
113
+ geometry: function () {
114
+ return null;
115
+ },
116
+ };
117
+ }
118
+
89
119
  return class BinaryRow {
90
120
  constructor() {}
91
121
 
@@ -110,7 +140,10 @@ function getBinaryParser(fields, _options, config) {
110
140
 
111
141
  let value;
112
142
  if (nullBitmaskBytes[nullByteIndex] & currentFieldNullBit) {
113
- value = null;
143
+ value =
144
+ typeof typeCast === 'function'
145
+ ? typeCast(wrapNull(field), () => null)
146
+ : null;
114
147
  } else if (options.typeCast === false) {
115
148
  value = packet.readLengthCodedBuffer();
116
149
  } else {
@@ -120,6 +153,8 @@ function getBinaryParser(fields, _options, config) {
120
153
  ? typeCast(
121
154
  {
122
155
  type: typeNames[field.columnType],
156
+ extendedTypeName: field.extendedTypeName,
157
+ extendedFormat: field.extendedFormat,
123
158
  length: field.columnLength,
124
159
  db: field.schema,
125
160
  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
+ : packet.parseJson(encoding, supportBigNumbers);
36
+ }
37
+
22
38
  switch (type) {
23
39
  case Types.TINY:
24
40
  case Types.SHORT:
@@ -64,7 +80,7 @@ function readField({ packet, type, charset, encoding, config, options }) {
64
80
  // see https://github.com/sidorares/node-mysql2/issues/409
65
81
  return config.jsonStrings
66
82
  ? packet.readLengthCodedString('utf8')
67
- : JSON.parse(packet.readLengthCodedString('utf8'));
83
+ : packet.parseJson('utf8', supportBigNumbers);
68
84
  default:
69
85
  if (charset === Charsets.BINARY) {
70
86
  return packet.readLengthCodedBuffer();
@@ -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
+ : `packet.parseJson(${encodingExpr}, ${supportBigNumbers})`;
32
+ }
33
+
24
34
  switch (type) {
25
35
  case Types.TINY:
26
36
  case Types.SHORT:
@@ -67,7 +77,7 @@ function readCodeFor(type, charset, encodingExpr, config, options) {
67
77
  // see https://github.com/sidorares/node-mysql2/issues/409
68
78
  return config.jsonStrings
69
79
  ? 'packet.readLengthCodedString("utf8")'
70
- : 'JSON.parse(packet.readLengthCodedString("utf8"))';
80
+ : `packet.parseJson("utf8", ${supportBigNumbers})`;
71
81
  default:
72
82
  if (charset === Charsets.BINARY) {
73
83
  return 'packet.readLengthCodedBuffer()';
@@ -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} });`
@@ -0,0 +1,25 @@
1
+ 'use strict';
2
+
3
+ /** @param {Function} constructorOpt Passed to Error.captureStackTrace (omit this frame from stacks). */
4
+ function captureStackHolder(constructorOpt) {
5
+ const holder = {};
6
+ Error.captureStackTrace(holder, constructorOpt);
7
+ return holder;
8
+ }
9
+
10
+ /**
11
+ * Replace `err.stack` frames with the capture from `holder`, keeping the
12
+ * callback error instance and its MySQL fields.
13
+ *
14
+ * @param {Error} err
15
+ * @param {{ stack?: string }} holder
16
+ */
17
+ function applyCapturedStack(err, holder) {
18
+ const stack = holder && holder.stack;
19
+ if (typeof stack !== 'string' || !stack) return;
20
+ const lines = stack.split('\n');
21
+ lines[0] = `${err.name}: ${err.message}`;
22
+ err.stack = lines.join('\n');
23
+ }
24
+
25
+ module.exports = { captureStackHolder, applyCapturedStack };
@@ -2,6 +2,10 @@
2
2
 
3
3
  const EventEmitter = require('events').EventEmitter;
4
4
  const PromisePreparedStatementInfo = require('./prepared_statement_info.js');
5
+ const {
6
+ captureStackHolder,
7
+ applyCapturedStack,
8
+ } = require('./capture_local_err.js');
5
9
  const makeDoneCb = require('./make_done_cb.js');
6
10
  const inheritEvents = require('./inherit_events.js');
7
11
  const BaseConnection = require('../base/connection.js');
@@ -26,13 +30,14 @@ class PromiseConnection extends EventEmitter {
26
30
 
27
31
  query(query, params) {
28
32
  const c = this.connection;
33
+ const stackHolder = captureStackHolder(PromiseConnection.prototype.query);
29
34
  if (typeof params === 'function') {
30
35
  throw new Error(
31
36
  'Callback function is not available with promise clients.'
32
37
  );
33
38
  }
34
39
  return new this.Promise((resolve, reject) => {
35
- const done = makeDoneCb(resolve, reject);
40
+ const done = makeDoneCb(resolve, reject, stackHolder);
36
41
  if (params !== undefined) {
37
42
  c.query(query, params, done);
38
43
  } else {
@@ -43,13 +48,14 @@ class PromiseConnection extends EventEmitter {
43
48
 
44
49
  execute(query, params) {
45
50
  const c = this.connection;
51
+ const stackHolder = captureStackHolder(PromiseConnection.prototype.execute);
46
52
  if (typeof params === 'function') {
47
53
  throw new Error(
48
54
  'Callback function is not available with promise clients.'
49
55
  );
50
56
  }
51
57
  return new this.Promise((resolve, reject) => {
52
- const done = makeDoneCb(resolve, reject);
58
+ const done = makeDoneCb(resolve, reject, stackHolder);
53
59
  if (params !== undefined) {
54
60
  c.execute(query, params, done);
55
61
  } else {
@@ -72,40 +78,43 @@ class PromiseConnection extends EventEmitter {
72
78
 
73
79
  beginTransaction() {
74
80
  const c = this.connection;
81
+ const stackHolder = captureStackHolder(
82
+ PromiseConnection.prototype.beginTransaction
83
+ );
75
84
  return new this.Promise((resolve, reject) => {
76
- const done = makeDoneCb(resolve, reject);
85
+ const done = makeDoneCb(resolve, reject, stackHolder);
77
86
  c.beginTransaction(done);
78
87
  });
79
88
  }
80
89
 
81
90
  commit() {
82
91
  const c = this.connection;
92
+ const stackHolder = captureStackHolder(PromiseConnection.prototype.commit);
83
93
  return new this.Promise((resolve, reject) => {
84
- const done = makeDoneCb(resolve, reject);
94
+ const done = makeDoneCb(resolve, reject, stackHolder);
85
95
  c.commit(done);
86
96
  });
87
97
  }
88
98
 
89
99
  rollback() {
90
100
  const c = this.connection;
101
+ const stackHolder = captureStackHolder(
102
+ PromiseConnection.prototype.rollback
103
+ );
91
104
  return new this.Promise((resolve, reject) => {
92
- const done = makeDoneCb(resolve, reject);
105
+ const done = makeDoneCb(resolve, reject, stackHolder);
93
106
  c.rollback(done);
94
107
  });
95
108
  }
96
109
 
97
110
  ping() {
98
111
  const c = this.connection;
112
+ const stackHolder = captureStackHolder(PromiseConnection.prototype.ping);
99
113
  return new this.Promise((resolve, reject) => {
100
114
  c.ping((err) => {
101
115
  if (err) {
102
- const localErr = new Error();
103
- localErr.message = err.message;
104
- localErr.code = err.code;
105
- localErr.errno = err.errno;
106
- localErr.sqlState = err.sqlState;
107
- localErr.sqlMessage = err.sqlMessage;
108
- reject(localErr);
116
+ applyCapturedStack(err, stackHolder);
117
+ reject(err);
109
118
  } else {
110
119
  resolve(true);
111
120
  }
@@ -115,16 +124,12 @@ class PromiseConnection extends EventEmitter {
115
124
 
116
125
  reset() {
117
126
  const c = this.connection;
127
+ const stackHolder = captureStackHolder(PromiseConnection.prototype.reset);
118
128
  return new this.Promise((resolve, reject) => {
119
129
  c.reset((err) => {
120
130
  if (err) {
121
- const localErr = new Error();
122
- localErr.message = err.message;
123
- localErr.code = err.code;
124
- localErr.errno = err.errno;
125
- localErr.sqlState = err.sqlState;
126
- localErr.sqlMessage = err.sqlMessage;
127
- reject(localErr);
131
+ applyCapturedStack(err, stackHolder);
132
+ reject(err);
128
133
  } else {
129
134
  resolve();
130
135
  }
@@ -134,16 +139,12 @@ class PromiseConnection extends EventEmitter {
134
139
 
135
140
  connect() {
136
141
  const c = this.connection;
142
+ const stackHolder = captureStackHolder(PromiseConnection.prototype.connect);
137
143
  return new this.Promise((resolve, reject) => {
138
144
  c.connect((err, param) => {
139
145
  if (err) {
140
- const localErr = new Error();
141
- localErr.message = err.message;
142
- localErr.code = err.code;
143
- localErr.errno = err.errno;
144
- localErr.sqlState = err.sqlState;
145
- localErr.sqlMessage = err.sqlMessage;
146
- reject(localErr);
146
+ applyCapturedStack(err, stackHolder);
147
+ reject(err);
147
148
  } else {
148
149
  resolve(param);
149
150
  }
@@ -154,16 +155,12 @@ class PromiseConnection extends EventEmitter {
154
155
  prepare(options) {
155
156
  const c = this.connection;
156
157
  const promiseImpl = this.Promise;
158
+ const stackHolder = captureStackHolder(PromiseConnection.prototype.prepare);
157
159
  return new this.Promise((resolve, reject) => {
158
160
  c.prepare(options, (err, statement) => {
159
161
  if (err) {
160
- const localErr = new Error();
161
- localErr.message = err.message;
162
- localErr.code = err.code;
163
- localErr.errno = err.errno;
164
- localErr.sqlState = err.sqlState;
165
- localErr.sqlMessage = err.sqlMessage;
166
- reject(localErr);
162
+ applyCapturedStack(err, stackHolder);
163
+ reject(err);
167
164
  } else {
168
165
  const wrappedStatement = new PromisePreparedStatementInfo(
169
166
  statement,
@@ -177,16 +174,14 @@ class PromiseConnection extends EventEmitter {
177
174
 
178
175
  changeUser(options) {
179
176
  const c = this.connection;
177
+ const stackHolder = captureStackHolder(
178
+ PromiseConnection.prototype.changeUser
179
+ );
180
180
  return new this.Promise((resolve, reject) => {
181
181
  c.changeUser(options, (err) => {
182
182
  if (err) {
183
- const localErr = new Error();
184
- localErr.message = err.message;
185
- localErr.code = err.code;
186
- localErr.errno = err.errno;
187
- localErr.sqlState = err.sqlState;
188
- localErr.sqlMessage = err.sqlMessage;
189
- reject(localErr);
183
+ applyCapturedStack(err, stackHolder);
184
+ reject(err);
190
185
  } else {
191
186
  resolve();
192
187
  }
@@ -1,16 +1,12 @@
1
1
  'use strict';
2
2
 
3
- function makeDoneCb(resolve, reject) {
3
+ const { applyCapturedStack } = require('./capture_local_err.js');
4
+
5
+ function makeDoneCb(resolve, reject, stackHolder) {
4
6
  return function (err, rows, fields) {
5
7
  if (err) {
6
- const localErr = new Error();
7
- localErr.message = err.message;
8
- localErr.code = err.code;
9
- localErr.errno = err.errno;
10
- localErr.sql = err.sql;
11
- localErr.sqlState = err.sqlState;
12
- localErr.sqlMessage = err.sqlMessage;
13
- reject(localErr);
8
+ applyCapturedStack(err, stackHolder);
9
+ reject(err);
14
10
  } else {
15
11
  resolve([rows, fields]);
16
12
  }
@@ -1,6 +1,10 @@
1
1
  'use strict';
2
2
 
3
3
  const EventEmitter = require('events').EventEmitter;
4
+ const {
5
+ captureStackHolder,
6
+ applyCapturedStack,
7
+ } = require('./capture_local_err.js');
4
8
  const makeDoneCb = require('./make_done_cb.js');
5
9
  const PromisePoolConnection = require('./pool_connection.js');
6
10
  const inheritEvents = require('./inherit_events.js');
@@ -33,13 +37,14 @@ class PromisePool extends EventEmitter {
33
37
 
34
38
  query(sql, args) {
35
39
  const corePool = this.pool;
40
+ const stackHolder = captureStackHolder(PromisePool.prototype.query);
36
41
  if (typeof args === 'function') {
37
42
  throw new Error(
38
43
  'Callback function is not available with promise clients.'
39
44
  );
40
45
  }
41
46
  return new this.Promise((resolve, reject) => {
42
- const done = makeDoneCb(resolve, reject);
47
+ const done = makeDoneCb(resolve, reject, stackHolder);
43
48
  if (args !== undefined) {
44
49
  corePool.query(sql, args, done);
45
50
  } else {
@@ -50,13 +55,14 @@ class PromisePool extends EventEmitter {
50
55
 
51
56
  execute(sql, args) {
52
57
  const corePool = this.pool;
58
+ const stackHolder = captureStackHolder(PromisePool.prototype.execute);
53
59
  if (typeof args === 'function') {
54
60
  throw new Error(
55
61
  'Callback function is not available with promise clients.'
56
62
  );
57
63
  }
58
64
  return new this.Promise((resolve, reject) => {
59
- const done = makeDoneCb(resolve, reject);
65
+ const done = makeDoneCb(resolve, reject, stackHolder);
60
66
  if (args) {
61
67
  corePool.execute(sql, args, done);
62
68
  } else {
@@ -67,16 +73,12 @@ class PromisePool extends EventEmitter {
67
73
 
68
74
  end() {
69
75
  const corePool = this.pool;
76
+ const stackHolder = captureStackHolder(PromisePool.prototype.end);
70
77
  return new this.Promise((resolve, reject) => {
71
78
  corePool.end((err) => {
72
79
  if (err) {
73
- const localErr = new Error();
74
- localErr.message = err.message;
75
- localErr.code = err.code;
76
- localErr.errno = err.errno;
77
- localErr.sqlState = err.sqlState;
78
- localErr.sqlMessage = err.sqlMessage;
79
- reject(localErr);
80
+ applyCapturedStack(err, stackHolder);
81
+ reject(err);
80
82
  } else {
81
83
  resolve();
82
84
  }
@@ -1,5 +1,6 @@
1
1
  'use strict';
2
2
 
3
+ const { captureStackHolder } = require('./capture_local_err.js');
3
4
  const PromisePoolConnection = require('./pool_connection');
4
5
  const makeDoneCb = require('./make_done_cb');
5
6
 
@@ -24,26 +25,32 @@ class PromisePoolNamespace {
24
25
 
25
26
  query(sql, values) {
26
27
  const corePoolNamespace = this.poolNamespace;
28
+ const stackHolder = captureStackHolder(
29
+ PromisePoolNamespace.prototype.query
30
+ );
27
31
  if (typeof values === 'function') {
28
32
  throw new Error(
29
33
  'Callback function is not available with promise clients.'
30
34
  );
31
35
  }
32
36
  return new this.Promise((resolve, reject) => {
33
- const done = makeDoneCb(resolve, reject);
37
+ const done = makeDoneCb(resolve, reject, stackHolder);
34
38
  corePoolNamespace.query(sql, values, done);
35
39
  });
36
40
  }
37
41
 
38
42
  execute(sql, values) {
39
43
  const corePoolNamespace = this.poolNamespace;
44
+ const stackHolder = captureStackHolder(
45
+ PromisePoolNamespace.prototype.execute
46
+ );
40
47
  if (typeof values === 'function') {
41
48
  throw new Error(
42
49
  'Callback function is not available with promise clients.'
43
50
  );
44
51
  }
45
52
  return new this.Promise((resolve, reject) => {
46
- const done = makeDoneCb(resolve, reject);
53
+ const done = makeDoneCb(resolve, reject, stackHolder);
47
54
  corePoolNamespace.execute(sql, values, done);
48
55
  });
49
56
  }
@@ -1,5 +1,6 @@
1
1
  'use strict';
2
2
 
3
+ const { captureStackHolder } = require('./capture_local_err.js');
3
4
  const makeDoneCb = require('./make_done_cb.js');
4
5
 
5
6
  class PromisePreparedStatementInfo {
@@ -10,8 +11,11 @@ class PromisePreparedStatementInfo {
10
11
 
11
12
  execute(parameters) {
12
13
  const s = this.statement;
14
+ const stackHolder = captureStackHolder(
15
+ PromisePreparedStatementInfo.prototype.execute
16
+ );
13
17
  return new this.Promise((resolve, reject) => {
14
- const done = makeDoneCb(resolve, reject);
18
+ const done = makeDoneCb(resolve, reject, stackHolder);
15
19
  if (parameters) {
16
20
  s.execute(parameters, done);
17
21
  } else {