Skip to content

Commit 5b6206c

Browse files
authored
fix: async stack traces not pointing to correct source, regression introduced by #4257 (#4265)
This reverts commit ab131de.
1 parent d1d5561 commit 5b6206c

7 files changed

Lines changed: 53 additions & 34 deletions

File tree

lib/promise/connection.js

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,14 @@ class PromiseConnection extends EventEmitter {
2626

2727
query(query, params) {
2828
const c = this.connection;
29+
const localErr = new Error();
2930
if (typeof params === 'function') {
3031
throw new Error(
3132
'Callback function is not available with promise clients.'
3233
);
3334
}
3435
return new this.Promise((resolve, reject) => {
35-
const done = makeDoneCb(resolve, reject);
36+
const done = makeDoneCb(resolve, reject, localErr);
3637
if (params !== undefined) {
3738
c.query(query, params, done);
3839
} else {
@@ -43,13 +44,14 @@ class PromiseConnection extends EventEmitter {
4344

4445
execute(query, params) {
4546
const c = this.connection;
47+
const localErr = new Error();
4648
if (typeof params === 'function') {
4749
throw new Error(
4850
'Callback function is not available with promise clients.'
4951
);
5052
}
5153
return new this.Promise((resolve, reject) => {
52-
const done = makeDoneCb(resolve, reject);
54+
const done = makeDoneCb(resolve, reject, localErr);
5355
if (params !== undefined) {
5456
c.execute(query, params, done);
5557
} else {
@@ -72,34 +74,37 @@ class PromiseConnection extends EventEmitter {
7274

7375
beginTransaction() {
7476
const c = this.connection;
77+
const localErr = new Error();
7578
return new this.Promise((resolve, reject) => {
76-
const done = makeDoneCb(resolve, reject);
79+
const done = makeDoneCb(resolve, reject, localErr);
7780
c.beginTransaction(done);
7881
});
7982
}
8083

8184
commit() {
8285
const c = this.connection;
86+
const localErr = new Error();
8387
return new this.Promise((resolve, reject) => {
84-
const done = makeDoneCb(resolve, reject);
88+
const done = makeDoneCb(resolve, reject, localErr);
8589
c.commit(done);
8690
});
8791
}
8892

8993
rollback() {
9094
const c = this.connection;
95+
const localErr = new Error();
9196
return new this.Promise((resolve, reject) => {
92-
const done = makeDoneCb(resolve, reject);
97+
const done = makeDoneCb(resolve, reject, localErr);
9398
c.rollback(done);
9499
});
95100
}
96101

97102
ping() {
98103
const c = this.connection;
104+
const localErr = new Error();
99105
return new this.Promise((resolve, reject) => {
100106
c.ping((err) => {
101107
if (err) {
102-
const localErr = new Error();
103108
localErr.message = err.message;
104109
localErr.code = err.code;
105110
localErr.errno = err.errno;
@@ -115,10 +120,10 @@ class PromiseConnection extends EventEmitter {
115120

116121
reset() {
117122
const c = this.connection;
123+
const localErr = new Error();
118124
return new this.Promise((resolve, reject) => {
119125
c.reset((err) => {
120126
if (err) {
121-
const localErr = new Error();
122127
localErr.message = err.message;
123128
localErr.code = err.code;
124129
localErr.errno = err.errno;
@@ -134,10 +139,10 @@ class PromiseConnection extends EventEmitter {
134139

135140
connect() {
136141
const c = this.connection;
142+
const localErr = new Error();
137143
return new this.Promise((resolve, reject) => {
138144
c.connect((err, param) => {
139145
if (err) {
140-
const localErr = new Error();
141146
localErr.message = err.message;
142147
localErr.code = err.code;
143148
localErr.errno = err.errno;
@@ -154,10 +159,10 @@ class PromiseConnection extends EventEmitter {
154159
prepare(options) {
155160
const c = this.connection;
156161
const promiseImpl = this.Promise;
162+
const localErr = new Error();
157163
return new this.Promise((resolve, reject) => {
158164
c.prepare(options, (err, statement) => {
159165
if (err) {
160-
const localErr = new Error();
161166
localErr.message = err.message;
162167
localErr.code = err.code;
163168
localErr.errno = err.errno;
@@ -177,10 +182,10 @@ class PromiseConnection extends EventEmitter {
177182

178183
changeUser(options) {
179184
const c = this.connection;
185+
const localErr = new Error();
180186
return new this.Promise((resolve, reject) => {
181187
c.changeUser(options, (err) => {
182188
if (err) {
183-
const localErr = new Error();
184189
localErr.message = err.message;
185190
localErr.code = err.code;
186191
localErr.errno = err.errno;

lib/promise/make_done_cb.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
'use strict';
22

3-
function makeDoneCb(resolve, reject) {
3+
function makeDoneCb(resolve, reject, localErr) {
44
return function (err, rows, fields) {
55
if (err) {
6-
const localErr = new Error();
76
localErr.message = err.message;
87
localErr.code = err.code;
98
localErr.errno = err.errno;

lib/promise/pool.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,14 @@ class PromisePool extends EventEmitter {
3333

3434
query(sql, args) {
3535
const corePool = this.pool;
36+
const localErr = new Error();
3637
if (typeof args === 'function') {
3738
throw new Error(
3839
'Callback function is not available with promise clients.'
3940
);
4041
}
4142
return new this.Promise((resolve, reject) => {
42-
const done = makeDoneCb(resolve, reject);
43+
const done = makeDoneCb(resolve, reject, localErr);
4344
if (args !== undefined) {
4445
corePool.query(sql, args, done);
4546
} else {
@@ -50,13 +51,14 @@ class PromisePool extends EventEmitter {
5051

5152
execute(sql, args) {
5253
const corePool = this.pool;
54+
const localErr = new Error();
5355
if (typeof args === 'function') {
5456
throw new Error(
5557
'Callback function is not available with promise clients.'
5658
);
5759
}
5860
return new this.Promise((resolve, reject) => {
59-
const done = makeDoneCb(resolve, reject);
61+
const done = makeDoneCb(resolve, reject, localErr);
6062
if (args) {
6163
corePool.execute(sql, args, done);
6264
} else {
@@ -67,10 +69,10 @@ class PromisePool extends EventEmitter {
6769

6870
end() {
6971
const corePool = this.pool;
72+
const localErr = new Error();
7073
return new this.Promise((resolve, reject) => {
7174
corePool.end((err) => {
7275
if (err) {
73-
const localErr = new Error();
7476
localErr.message = err.message;
7577
localErr.code = err.code;
7678
localErr.errno = err.errno;

lib/promise/pool_cluster.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,26 +24,28 @@ class PromisePoolNamespace {
2424

2525
query(sql, values) {
2626
const corePoolNamespace = this.poolNamespace;
27+
const localErr = new Error();
2728
if (typeof values === 'function') {
2829
throw new Error(
2930
'Callback function is not available with promise clients.'
3031
);
3132
}
3233
return new this.Promise((resolve, reject) => {
33-
const done = makeDoneCb(resolve, reject);
34+
const done = makeDoneCb(resolve, reject, localErr);
3435
corePoolNamespace.query(sql, values, done);
3536
});
3637
}
3738

3839
execute(sql, values) {
3940
const corePoolNamespace = this.poolNamespace;
41+
const localErr = new Error();
4042
if (typeof values === 'function') {
4143
throw new Error(
4244
'Callback function is not available with promise clients.'
4345
);
4446
}
4547
return new this.Promise((resolve, reject) => {
46-
const done = makeDoneCb(resolve, reject);
48+
const done = makeDoneCb(resolve, reject, localErr);
4749
corePoolNamespace.execute(sql, values, done);
4850
});
4951
}

lib/promise/prepared_statement_info.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,9 @@ class PromisePreparedStatementInfo {
1010

1111
execute(parameters) {
1212
const s = this.statement;
13+
const localErr = new Error();
1314
return new this.Promise((resolve, reject) => {
14-
const done = makeDoneCb(resolve, reject);
15+
const done = makeDoneCb(resolve, reject, localErr);
1516
if (parameters) {
1617
s.execute(parameters, done);
1718
} else {

promise.js

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ const PromisePoolNamespace = require('./lib/promise/pool_cluster');
1616

1717
function createConnectionPromise(opts) {
1818
const coreConnection = createConnection(opts);
19+
const createConnectionErr = new Error();
1920
const thePromise = opts.Promise || Promise;
2021
if (!thePromise) {
2122
throw new Error(
@@ -29,7 +30,6 @@ function createConnectionPromise(opts) {
2930
resolve(new PromiseConnection(coreConnection, thePromise));
3031
});
3132
coreConnection.once('error', (err) => {
32-
const createConnectionErr = new Error();
3333
createConnectionErr.message = err.message;
3434
createConnectionErr.code = err.code;
3535
createConnectionErr.errno = err.errno;
@@ -83,26 +83,28 @@ class PromisePoolCluster extends EventEmitter {
8383

8484
query(sql, args) {
8585
const corePoolCluster = this.poolCluster;
86+
const localErr = new Error();
8687
if (typeof args === 'function') {
8788
throw new Error(
8889
'Callback function is not available with promise clients.'
8990
);
9091
}
9192
return new this.Promise((resolve, reject) => {
92-
const done = makeDoneCb(resolve, reject);
93+
const done = makeDoneCb(resolve, reject, localErr);
9394
corePoolCluster.query(sql, args, done);
9495
});
9596
}
9697

9798
execute(sql, args) {
9899
const corePoolCluster = this.poolCluster;
100+
const localErr = new Error();
99101
if (typeof args === 'function') {
100102
throw new Error(
101103
'Callback function is not available with promise clients.'
102104
);
103105
}
104106
return new this.Promise((resolve, reject) => {
105-
const done = makeDoneCb(resolve, reject);
107+
const done = makeDoneCb(resolve, reject, localErr);
106108
corePoolCluster.execute(sql, args, done);
107109
});
108110
}
@@ -116,10 +118,10 @@ class PromisePoolCluster extends EventEmitter {
116118

117119
end() {
118120
const corePoolCluster = this.poolCluster;
121+
const localErr = new Error();
119122
return new this.Promise((resolve, reject) => {
120123
corePoolCluster.end((err) => {
121124
if (err) {
122-
const localErr = new Error();
123125
localErr.message = err.message;
124126
localErr.code = err.code;
125127
localErr.errno = err.errno;

test/integration/promise-wrappers/test-async-stack.test.mts

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { ConnectionOptions } from '../../../index.js';
22
import process from 'node:process';
3+
import ErrorStackParser from 'error-stack-parser';
34
import { describe, it, skip, strict } from 'poku';
45
import { createConnection as promiseCreateConnection } from '../../../promise.js';
56
import { config } from '../../common.test.mjs';
@@ -14,27 +15,34 @@ await describe('Async stack traces', async () => {
1415
return promiseCreateConnection({ ...config, ...args });
1516
};
1617

17-
await it('should propagate connection error with code and message', async () => {
18+
// TODO: investigate why connection is still open after ENETUNREACH
19+
await it('should include caller stack in connection error', async () => {
20+
let e1: Error;
1821
try {
22+
e1 = new Error();
23+
// expected not to connect
1924
await createConnection({ host: '127.0.0.1', port: 33066 });
20-
strict(false, 'Expected connection to fail');
2125
} catch (err) {
22-
strict(err instanceof Error);
23-
strict((err as Error & { code?: string }).code === 'ECONNREFUSED');
24-
strict(typeof (err as Error).stack === 'string');
26+
const stack = ErrorStackParser.parse(err as Error);
27+
const stackExpected = ErrorStackParser.parse(e1!);
28+
strict(
29+
stack[2].getLineNumber() === (stackExpected[0].getLineNumber() ?? 0) + 2
30+
);
2531
}
2632
});
2733

28-
await it('should propagate query error with code and message', async () => {
34+
await it('should include caller stack in query error', async () => {
2935
const conn = await createConnection();
36+
let e2: Error;
3037
try {
31-
await conn.query('syntax error');
32-
strict(false, 'Expected query to fail');
38+
e2 = new Error();
39+
await Promise.all([conn.query('select 1+1'), conn.query('syntax error')]);
3340
} catch (err) {
34-
strict(err instanceof Error);
35-
strict((err as Error & { code?: string }).code === 'ER_PARSE_ERROR');
36-
strict(typeof (err as Error).message === 'string');
37-
strict(typeof (err as Error).stack === 'string');
41+
const stack = ErrorStackParser.parse(err as Error);
42+
const stackExpected = ErrorStackParser.parse(e2!);
43+
strict(
44+
stack[1].getLineNumber() === (stackExpected[0].getLineNumber() ?? 0) + 1
45+
);
3846
} finally {
3947
await conn.end();
4048
}

0 commit comments

Comments
 (0)