Socket
Socket
Sign inDemoInstall

mysql2

Package Overview
Dependencies
Maintainers
3
Versions
184
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

mysql2 - npm Package Compare versions

Comparing version 2.2.5 to 2.3.0

17

Changelog.md

@@ -0,1 +1,18 @@

2.3.0
- Add PoolCluster promise wrappers #1369, #1363
- support for connect and query timeouts #1364
- add missing query() method on PoolCluster #1362
- fix incorrect parsing of passwords
containing ":" #1357
- handle errors generated by asynchronous
authentication plugins #1354
- add proper handshake fatal error handling #1352
- fix tests to work with the latest MySQL
server versions (up to 8.0.25) #1338
- expose SQL query in errors #1295
- typing and readme docs for rowAsArray #1288
- allow unnamed placeholders even if the
namedPlaceholders flag is enabled #1251
- better ESM support #1217
2.2.5 ( 21/09/2020 )

@@ -2,0 +19,0 @@ - typings: add ResultSetHeader #1213

11

index.d.ts

@@ -154,5 +154,8 @@ import {

type authPlugins =
(pluginMetadata: { connection: Connection; command: string }) =>
(pluginData: Buffer) => Promise<string>;
type authPlugins = (pluginMetadata: {
connection: Connection;
command: string;
}) => (
pluginData: Buffer
) => Promise<string> | string | Buffer | Promise<Buffer> | null;

@@ -179,3 +182,3 @@ export interface ConnectionOptions extends mysql.ConnectionOptions {

authPlugins?: {
[key: string]: authPlugins;
[key: string]: authPlugins;
};

@@ -182,0 +185,0 @@ }

@@ -17,2 +17,3 @@ 'use strict';

const Pool = require('./lib/pool.js');
const PoolCluster = require('./lib/pool_cluster.js');

@@ -33,2 +34,4 @@ exports.createPool = function(config) {

exports.PoolCluster = PoolCluster;
exports.createServer = function(handler) {

@@ -35,0 +38,0 @@ const Server = require('./lib/server.js');

@@ -0,1 +1,6 @@

// This file was modified by Oracle on July 5, 2021.
// Errors generated by asynchronous authentication plugins are now being
// handled and subsequently emitted at the command level.
// Modifications copyright (c) 2021, Oracle and/or its affiliates.
'use strict';

@@ -20,2 +25,10 @@

function authSwitchPluginError(error, command) {
// Authentication errors are fatal
error.code = 'AUTH_SWITCH_PLUGIN_ERROR';
error.fatal = true;
command.emit('error', error);
}
function authSwitchRequest(packet, connection, command) {

@@ -38,4 +51,3 @@ const { pluginName, pluginData } = Packets.AuthSwitchRequest.fromPacket(

if (err) {
connection.emit('error', err);
return;
return authSwitchPluginError(err, command);
}

@@ -59,6 +71,8 @@ connection.writePacket(new Packets.AuthSwitchResponse(data).toPacket());

}
}).catch(err => {
authSwitchPluginError(err, command);
});
}
function authSwitchRequestMoreData(packet, connection) {
function authSwitchRequestMoreData(packet, connection, command) {
const { data } = Packets.AuthSwitchRequestMoreData.fromPacket(packet);

@@ -71,4 +85,3 @@

if (err) {
connection.emit('error', err);
return;
return authSwitchPluginError(err, command);
}

@@ -89,2 +102,4 @@ connection.writePacket(new Packets.AuthSwitchResponse(data).toPacket());

}
}).catch(err => {
authSwitchPluginError(err, command);
});

@@ -91,0 +106,0 @@ }

@@ -0,1 +1,6 @@

// This file was modified by Oracle on June 17, 2021.
// Handshake errors are now maked as fatal and the corresponding events are
// emitted in the command instance itself.
// Modifications copyright (c) 2021, Oracle and/or its affiliates.
'use strict';

@@ -154,6 +159,10 @@

} catch (err) {
// Authentication errors are fatal
err.code = 'AUTH_SWITCH_PLUGIN_ERROR';
err.fatal = true;
if (this.onResult) {
this.onResult(err);
} else {
connection.emit('error', err);
this.emit('error', err);
}

@@ -165,6 +174,10 @@ return null;

const err = new Error('Unexpected packet during handshake phase');
// Unknown handshake errors are fatal
err.code = 'HANDSHAKE_UNKNOWN_ERROR';
err.fatal = true;
if (this.onResult) {
this.onResult(err);
} else {
connection.emit('error', err);
this.emit('error', err);
}

@@ -171,0 +184,0 @@ return null;

@@ -29,2 +29,3 @@ 'use strict';

const err = packet.asError(connection.clientEncoding);
err.sql = this.sql || this.query;
if (this.onResult) {

@@ -31,0 +32,0 @@ this.onResult(err);

@@ -18,2 +18,4 @@ 'use strict';

this.insertId = 0;
this.timeout = options.timeout;
this.queryTimeout = null;
this._rows = [];

@@ -39,2 +41,3 @@ this._fields = [];

this.options = Object.assign({}, connection.config, this._executeOptions);
this._setTimeout();
const executePacket = new Packets.Execute(

@@ -101,2 +104,4 @@ this.statement.id,

Execute.prototype._streamLocalInfile = Query.prototype._streamLocalInfile;
Execute.prototype._setTimeout = Query.prototype._setTimeout;
Execute.prototype._handleTimeoutError = Query.prototype._handleTimeoutError;
Execute.prototype.row = Query.prototype.row;

@@ -103,0 +108,0 @@ Execute.prototype.stream = Query.prototype.stream;

'use strict';
const process = require('process');
const Timers = require('timers');

@@ -24,2 +25,4 @@ const Readable = require('stream').Readable;

this.onResult = callback;
this.timeout = options.timeout;
this.queryTimeout = null;
this._fieldCount = 0;

@@ -52,2 +55,4 @@ this._rowParser = null;

this.options = Object.assign({}, connection.config, this._queryOptions);
this._setTimeout();
const cmdPacket = new Packets.Query(

@@ -63,2 +68,11 @@ this.sql,

this._unpipeStream();
// if all ready timeout, return null directly
if (this.timeout && !this.queryTimeout) {
return null;
}
// else clear timer
if (this.queryTimeout) {
Timers.clearTimeout(this.queryTimeout);
this.queryTimeout = null;
}
if (this.onResult) {

@@ -278,2 +292,30 @@ let rows, fields;

}
_setTimeout() {
if (this.timeout) {
const timeoutHandler = this._handleTimeoutError.bind(this);
this.queryTimeout = Timers.setTimeout(
timeoutHandler,
this.timeout
);
}
}
_handleTimeoutError() {
if (this.queryTimeout) {
Timers.clearTimeout(this.queryTimeout);
this.queryTimeout = null;
}
const err = new Error('Query inactivity timeout');
err.errorno = 'PROTOCOL_SEQUENCE_TIMEOUT';
err.code = 'PROTOCOL_SEQUENCE_TIMEOUT';
err.syscall = 'query';
if (this.onResult) {
this.onResult(err);
} else {
this.emit('error', err);
}
}
}

@@ -280,0 +322,0 @@

'use strict';
const urlParse = require('url').parse;
const { URL } = require('url');
const ClientConstants = require('./constants/client');

@@ -235,25 +235,19 @@ const Charsets = require('./constants/charsets');

static parseUrl(url) {
url = urlParse(url, true);
const parsedUrl = new URL(url);
const options = {
host: url.hostname,
port: url.port,
database: url.pathname.substr(1)
host: parsedUrl.hostname,
port: parsedUrl.port,
database: parsedUrl.pathname.substr(1),
user: parsedUrl.username,
password: parsedUrl.password
};
if (url.auth) {
const auth = url.auth.split(':');
options.user = auth[0];
options.password = auth[1];
}
if (url.query) {
for (const key in url.query) {
const value = url.query[key];
try {
// Try to parse this as a JSON expression first
options[key] = JSON.parse(value);
} catch (err) {
// Otherwise assume it is a plain string
options[key] = value;
}
parsedUrl.searchParams.forEach((value, key) => {
try {
// Try to parse this as a JSON expression first
options[key] = JSON.parse(value);
} catch (err) {
// Otherwise assume it is a plain string
options[key] = value;
}
}
});
return options;

@@ -260,0 +254,0 @@ }

@@ -0,1 +1,11 @@

// This file was modified by Oracle on June 1, 2021.
// The changes involve new logic to handle an additional ERR Packet sent by
// the MySQL server when the connection is closed unexpectedly.
// Modifications copyright (c) 2021, Oracle and/or its affiliates.
// This file was modified by Oracle on June 17, 2021.
// The changes involve logic to ensure the socket connection is closed when
// there is a fatal error.
// Modifications copyright (c) 2021, Oracle and/or its affiliates.
'use strict';

@@ -103,5 +113,6 @@

handshakeCommand.on('end', () => {
// this happens when handshake finishes early and first packet is error
// and not server hello ( for example, 'Too many connactions' error)
if (!handshakeCommand.handshake) {
// this happens when handshake finishes early either because there was
// some fatal error or the server sent an error packet instead of
// an hello packet (for example, 'Too many connactions' error)
if (!handshakeCommand.handshake || this._fatalError || this._protocolError) {
return;

@@ -192,3 +203,3 @@ }

this.connectTimeout = null;
}
}
// prevent from emitting 'PROTOCOL_CONNECTION_LOST' after EPIPE or ECONNRESET

@@ -229,2 +240,6 @@ if (this._fatalError) {

}
// close connection after emitting the event in case of a fatal error
if (err.fatal) {
this.close();
}
}

@@ -374,2 +389,10 @@

protocolError(message, code) {
// Starting with MySQL 8.0.24, if the client closes the connection
// unexpectedly, the server will send a last ERR Packet, which we can
// safely ignore.
// https://dev.mysql.com/worklog/task/?id=12999
if (this._closing) {
return;
}
const err = new Error(message);

@@ -422,6 +445,14 @@ err.fatal = true;

if (!this._command) {
this.protocolError(
'Unexpected packet while no commands in the queue',
'PROTOCOL_UNEXPECTED_PACKET'
);
const marker = packet.peekByte();
// If it's an Err Packet, we should use it.
if (marker === 0xff) {
const error = Packets.Error.fromPacket(packet);
this.protocolError(error.message, error.code);
} else {
// Otherwise, it means it's some other unexpected packet.
this.protocolError(
'Unexpected packet while no commands in the queue',
'PROTOCOL_UNEXPECTED_PACKET'
);
}
this.close();

@@ -496,2 +527,7 @@ return;

if (this.config.namedPlaceholders || options.namedPlaceholders) {
if (Array.isArray(options.values)) {
// if an array is provided as the values, assume the conversion is not necessary.
// this allows the usage of unnamed placeholders even if the namedPlaceholders flag is enabled.
return
}
if (convertNamedPlaceholders === null) {

@@ -498,0 +534,0 @@ convertNamedPlaceholders = require('named-placeholders')();

@@ -0,1 +1,6 @@

// This file was modified by Oracle on June 1, 2021.
// A utility method was introduced to generate an Error instance from a
// binary server packet.
// Modifications copyright (c) 2021, Oracle and/or its affiliates.
'use strict';

@@ -125,4 +130,18 @@

}
static fromPacket(packet) {
packet.readInt8(); // marker
const code = packet.readInt16();
packet.readString(1, 'ascii'); // sql state marker
// The SQL state of the ERR_Packet which is always 5 bytes long.
// https://dev.mysql.com/doc/dev/mysql-server/8.0.11/page_protocol_basic_dt_strings.html#sect_protocol_basic_dt_string_fix
packet.readString(5, 'ascii'); // sql state (ignore for now)
const message = packet.readNullTerminatedString('utf8');
const error = new Error();
error.message = message;
error.code = code;
return error;
}
}
exports.Error = Error;

@@ -0,1 +1,6 @@

// This file was modified by Oracle on June 1, 2021.
// A comment describing some changes in the strict default SQL mode regarding
// non-standard dates was introduced.
// Modifications copyright (c) 2021, Oracle and/or its affiliates.
'use strict';

@@ -277,2 +282,7 @@

}
// NO_ZERO_DATE mode and NO_ZERO_IN_DATE mode are part of the strict
// default SQL mode used by MySQL 8.0. This means that non-standard
// dates like '0000-00-00' become NULL. For older versions and other
// possible MySQL flavours we still need to account for the
// non-standard behaviour.
if (y + m + d + H + M + S + ms === 0) {

@@ -279,0 +289,0 @@ return INVALID_DATE;

@@ -7,2 +7,3 @@ 'use strict';

const PoolConfig = require('./pool_config.js');
const Connection = require('./connection.js');
const EventEmitter = require('events').EventEmitter;

@@ -50,2 +51,58 @@

/**
* pool cluster query
* @param {*} sql
* @param {*} values
* @param {*} cb
* @returns query
*/
query(sql, values, cb) {
const query = Connection.createQuery(sql, values, cb, {});
this.getConnection((err, conn) => {
if (err) {
if (typeof query.onResult === 'function') {
query.onResult(err);
} else {
query.emit('error', err);
}
return;
}
try {
conn.query(query).once('end', () => {
conn.release();
});
} catch (e) {
conn.release();
throw e;
}
});
return query;
}
/**
* pool cluster execute
* @param {*} sql
* @param {*} values
* @param {*} cb
*/
execute(sql, values, cb) {
if (typeof values === 'function') {
cb = values;
values = [];
}
this.getConnection((err, conn) => {
if (err) {
return cb(err);
}
try {
conn.execute(sql, values, cb).once('end', () => {
conn.release();
});
} catch (e) {
conn.release();
throw e;
}
});
}
_getClusterNode() {

@@ -52,0 +109,0 @@ const foundNodeIds = this._cluster._findNodeIds(this._pattern);

{
"name": "mysql2",
"version": "2.2.5",
"version": "2.3.0",
"description": "fast mysql driver. Implements core protocol, prepared statements, ssl and compression in native JS",

@@ -47,3 +47,4 @@ "main": "index.js",

".": "./index.js",
"./promise": "./promise.js"
"./promise": "./promise.js",
"./promise.js": "./promise.js"
},

@@ -50,0 +51,0 @@ "engines": {

@@ -12,2 +12,3 @@ 'use strict';

localErr.errno = err.errno;
localErr.sql = err.sql;
localErr.sqlState = err.sqlState;

@@ -431,4 +432,116 @@ localErr.sqlMessage = err.sqlMessage;

class PromisePoolCluster extends EventEmitter {
constructor(poolCluster, thePromise) {
super();
this.poolCluster = poolCluster;
this.Promise = thePromise || Promise;
inheritEvents(poolCluster, this, ['acquire', 'connection', 'enqueue', 'release']);
}
getConnection() {
const corePoolCluster = this.poolCluster;
return new this.Promise((resolve, reject) => {
corePoolCluster.getConnection((err, coreConnection) => {
if (err) {
reject(err);
} else {
resolve(new PromisePoolConnection(coreConnection, this.Promise));
}
});
});
}
query(sql, args) {
const corePoolCluster = this.poolCluster;
const localErr = new Error();
if (typeof args === 'function') {
throw new Error(
'Callback function is not available with promise clients.'
);
}
return new this.Promise((resolve, reject) => {
const done = makeDoneCb(resolve, reject, localErr);
corePoolCluster.query(sql, args, done);
});
}
execute(sql, args) {
const corePoolCluster = this.poolCluster;
const localErr = new Error();
if (typeof args === 'function') {
throw new Error(
'Callback function is not available with promise clients.'
);
}
return new this.Promise((resolve, reject) => {
const done = makeDoneCb(resolve, reject, localErr);
corePoolCluster.execute(sql, args, done);
});
}
of(pattern, selector) {
return new PromisePoolCluster(
this.poolCluster.of(pattern, selector),
this.Promise
);
}
end() {
const corePoolCluster = this.poolCluster;
const localErr = new Error();
return new this.Promise((resolve, reject) => {
corePoolCluster.end(err => {
if (err) {
localErr.message = err.message;
localErr.code = err.code;
localErr.errno = err.errno;
localErr.sqlState = err.sqlState;
localErr.sqlMessage = err.sqlMessage;
reject(localErr);
} else {
resolve();
}
});
});
}
}
/**
* proxy poolCluster synchronous functions
*/
(function (functionsToWrap) {
for (let i = 0; functionsToWrap && i < functionsToWrap.length; i++) {
const func = functionsToWrap[i];
if (
typeof core.PoolCluster.prototype[func] === 'function' &&
PromisePoolCluster.prototype[func] === undefined
) {
PromisePoolCluster.prototype[func] = (function factory(funcName) {
return function () {
return core.PoolCluster.prototype[funcName].apply(this.poolCluster, arguments);
};
})(func);
}
}
})([
'add'
]);
function createPoolCluster(opts) {
const corePoolCluster = core.createPoolCluster(opts);
const thePromise = (opts && opts.Promise) || Promise;
if (!thePromise) {
throw new Error(
'no Promise implementation available.' +
'Use promise-enabled node version or pass userland Promise' +
" implementation as parameter, for example: { Promise: require('bluebird') }"
);
}
return new PromisePoolCluster(corePoolCluster, thePromise);
}
exports.createConnection = createConnection;
exports.createPool = createPool;
exports.createPoolCluster = createPoolCluster;
exports.escape = core.escape;

@@ -435,0 +548,0 @@ exports.escapeId = core.escapeId;

@@ -219,2 +219,28 @@ ## Node MySQL 2

## Array results
If you have two columns with the same name, you might want to get results as an array rather than an object to prevent them from clashing. This is a deviation from the [Node MySQL][node-mysql] library.
For example: `select 1 as foo, 2 as foo`.
You can enable this setting at either the connection level (applies to all queries), or at the query level (applies only to that specific query).
### Connection Option
```js
const con = mysql.createConnection(
{ host: 'localhost', database: 'test', user: 'root', rowsAsArray: true }
);
```
### Query Option
```js
con.query({ sql: 'select 1 as foo, 2 as foo', rowsAsArray: true }, function(err, results, fields) {
console.log(results) // will be an array of arrays rather than an array of objects
console.log(fields) // these are unchanged
});
```
## API and Configuration

@@ -221,0 +247,0 @@

@@ -150,2 +150,10 @@

ssl?: string | SslOptions;
/**
* Return each row as an array, not as an object.
* This is useful when you have duplicate column names.
* This can also be set in the `QueryOption` object to be applied per-query.
*/
rowsAsArray?: boolean
}

@@ -200,2 +208,3 @@

threadId: number;
authorized: boolean;

@@ -202,0 +211,0 @@ static createQuery<T extends RowDataPacket[][] | RowDataPacket[] | OkPacket | OkPacket[] | ResultSetHeader>(sql: string, callback?: (err: Query.QueryError | null, result: T, fields: FieldPacket[]) => any): Query;

@@ -54,2 +54,8 @@

typeCast?: any;
/**
* This overrides the same option set at the connection level.
*
*/
rowsAsArray?: boolean
}

@@ -56,0 +62,0 @@

Sorry, the diff of this file is too big to display

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc