Skip to content

Commit 0124e92

Browse files
authored
fix: avoid fake .then() method on mysql2 Query class (#501)
* fix: avoid fake .then() method on mysql2 Query class * test: add unit test for fake .then() method on mysql2 Query class
1 parent a204a01 commit 0124e92

File tree

2 files changed

+67
-4
lines changed

2 files changed

+67
-4
lines changed

packages/mysql/lib/mysql_p.js

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,26 @@ module.exports = function captureMySQL(mysql) {
3333
return mysql;
3434
};
3535

36+
function isPromise(maybePromise) {
37+
if (maybePromise != null && maybePromise.then instanceof Function) {
38+
// mysql2 has a `Query` class with a `then` method which always throws an error when called.
39+
// We want to avoid calling this, so we need to check for more than just the presence of a `then` method.
40+
// See https://github.com/sidorares/node-mysql2/blob/dbb344e89a1cc8bb457b24e67b07cdb3013fe844/lib/commands/query.js#L38-L44
41+
// Since it's highly unlikely that any Promise implementation would name their class `Query`,
42+
// we can safely use this to determine whether or not this is actually a Promise.
43+
const constructorName = maybePromise.constructor != null ? maybePromise.constructor.name : undefined;
44+
return constructorName !== 'Query';
45+
}
46+
return false;
47+
}
48+
3649
function patchCreateConnection(mysql) {
3750
var baseFcn = '__createConnection';
3851
mysql[baseFcn] = mysql['createConnection'];
3952

4053
mysql['createConnection'] = function patchedCreateConnection() {
4154
var connection = mysql[baseFcn].apply(connection, arguments);
42-
if (connection && connection.then instanceof Function) {
55+
if (isPromise(connection)) {
4356
connection = connection.then((result) => {
4457
patchObject(result.connection);
4558
return result;
@@ -57,7 +70,7 @@ function patchCreatePool(mysql) {
5770

5871
mysql['createPool'] = function patchedCreatePool() {
5972
var pool = mysql[baseFcn].apply(pool, arguments);
60-
if (pool && pool.then instanceof Function) {
73+
if (isPromise(pool)) {
6174
pool = pool.then((result) => {
6275
patchObject(result.pool);
6376
return result;
@@ -112,7 +125,7 @@ function patchGetConnection(pool) {
112125
}
113126

114127
var result = pool[baseFcn].apply(pool, args);
115-
if (result && result.then instanceof Function) {
128+
if (isPromise(result)) {
116129
return result.then(patchObject);
117130
} else {
118131
return result;
@@ -232,7 +245,7 @@ function captureOperation(name) {
232245
}
233246
};
234247

235-
if (command.then instanceof Function) {
248+
if (isPromise(command)) {
236249
command.then(() => {
237250
subsegment.close();
238251
});

packages/mysql/test/unit/mysql_p.test.js

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ var sinon = require('sinon');
44
var sinonChai = require('sinon-chai');
55

66
var AWSXRay = require('aws-xray-sdk-core');
7+
var EventEmitter = require('events').EventEmitter;
78

89
var captureMySQL = require('../../lib/mysql_p');
910
var Segment = AWSXRay.Segment;
@@ -34,6 +35,55 @@ describe('captureMySQL', function() {
3435
assert.equal(patched.createPool.name, 'patchedCreatePool');
3536
assert.equal(patched.createPoolCluster.name, 'patchedCreatePoolCluster');
3637
});
38+
39+
describe('handles mysql2 quirks', function() {
40+
var conn, connectionObj, mysql, sandbox, segment;
41+
42+
// See https://github.com/sidorares/node-mysql2/blob/dbb344e89a1cc8bb457b24e67b07cdb3013fe844/lib/commands/query.js#L38-L44
43+
class Query extends EventEmitter {
44+
then() {
45+
throw new Error('You have tried to call .then(), .catch(), or invoked await on the result of query that is not a promise [...]');
46+
}
47+
}
48+
49+
before(function() {
50+
conn = {
51+
config: {
52+
user: 'mcmuls',
53+
host: 'database.location',
54+
port: '8080',
55+
database: 'myTestDb'
56+
},
57+
query: function() {
58+
return new Query();
59+
}
60+
};
61+
62+
mysql = { createConnection: function() {
63+
return conn;
64+
} };
65+
mysql = captureMySQL(mysql);
66+
connectionObj = mysql.createConnection();
67+
});
68+
69+
beforeEach(function() {
70+
sandbox = sinon.createSandbox();
71+
segment = new Segment('test');
72+
segment.addNewSubsegment('testSub');
73+
74+
sandbox = sinon.createSandbox();
75+
sandbox.stub(AWSXRay, 'getSegment').returns(segment);
76+
sandbox.stub(AWSXRay, 'isAutomaticMode').returns(true);
77+
});
78+
79+
afterEach(function() {
80+
sandbox.restore();
81+
});
82+
83+
it('should not call fake .then() on mysql2 Query class', function() {
84+
(() => connectionObj.query()).should.not.throw();
85+
});
86+
});
3787
});
3888

3989
describe('#captureQuery', function() {

0 commit comments

Comments
 (0)