Update Fix
This commit is contained in:
8
node_modules/mysql2/lib/auth_plugins/caching_sha2_password.js
generated
vendored
8
node_modules/mysql2/lib/auth_plugins/caching_sha2_password.js
generated
vendored
@@ -42,7 +42,7 @@ function encrypt(password, scramble, key) {
|
||||
);
|
||||
}
|
||||
|
||||
module.exports =
|
||||
const pluginFactory =
|
||||
(pluginOptions = {}) =>
|
||||
({ connection }) => {
|
||||
let state = 0;
|
||||
@@ -106,3 +106,9 @@ module.exports =
|
||||
);
|
||||
};
|
||||
};
|
||||
|
||||
// Export the plugin factory as default
|
||||
module.exports = pluginFactory;
|
||||
|
||||
// Export calculateToken for reuse in initial handshake optimization
|
||||
module.exports.calculateToken = calculateToken;
|
||||
|
||||
42
node_modules/mysql2/lib/base/connection.js
generated
vendored
42
node_modules/mysql2/lib/base/connection.js
generated
vendored
@@ -21,7 +21,7 @@ const Timers = require('timers');
|
||||
const EventEmitter = require('events').EventEmitter;
|
||||
const Readable = require('stream').Readable;
|
||||
const Queue = require('denque');
|
||||
const SqlString = require('sqlstring');
|
||||
const SqlString = require('sql-escaper');
|
||||
const { createLRU } = require('lru.min');
|
||||
const PacketParser = require('../packet_parser.js');
|
||||
const Packets = require('../packets/index.js');
|
||||
@@ -835,27 +835,31 @@ class BaseConnection extends EventEmitter {
|
||||
if (!cb) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._fatalError || this._protocolError) {
|
||||
return cb(this._fatalError || this._protocolError);
|
||||
}
|
||||
|
||||
if (this._handshakePacket) {
|
||||
return cb(null, this);
|
||||
}
|
||||
let connectCalled = 0;
|
||||
function callbackOnce(isErrorHandler) {
|
||||
return function (param) {
|
||||
if (!connectCalled) {
|
||||
if (isErrorHandler) {
|
||||
cb(param);
|
||||
} else {
|
||||
cb(null, param);
|
||||
}
|
||||
}
|
||||
connectCalled = 1;
|
||||
};
|
||||
}
|
||||
this.once('error', callbackOnce(true));
|
||||
this.once('connect', callbackOnce(false));
|
||||
|
||||
/* eslint-disable prefer-const */
|
||||
let onError, onConnect;
|
||||
|
||||
onError = (param) => {
|
||||
this.removeListener('connect', onConnect);
|
||||
cb(param);
|
||||
};
|
||||
|
||||
onConnect = (param) => {
|
||||
this.removeListener('error', onError);
|
||||
cb(null, param);
|
||||
};
|
||||
/* eslint-enable prefer-const */
|
||||
|
||||
this.once('error', onError);
|
||||
this.once('connect', onConnect);
|
||||
}
|
||||
|
||||
// ===================================
|
||||
@@ -922,6 +926,12 @@ class BaseConnection extends EventEmitter {
|
||||
return this.addCommand(new Commands.ServerHandshake(args));
|
||||
}
|
||||
|
||||
[Symbol.dispose]() {
|
||||
if (!this._closing) {
|
||||
this.end();
|
||||
}
|
||||
}
|
||||
|
||||
// ===============================================================
|
||||
end(callback) {
|
||||
if (this.config.isServer) {
|
||||
|
||||
55
node_modules/mysql2/lib/base/pool.js
generated
vendored
55
node_modules/mysql2/lib/base/pool.js
generated
vendored
@@ -1,11 +1,27 @@
|
||||
'use strict';
|
||||
|
||||
const process = require('process');
|
||||
const SqlString = require('sqlstring');
|
||||
const SqlString = require('sql-escaper');
|
||||
const EventEmitter = require('events').EventEmitter;
|
||||
const PoolConnection = require('../pool_connection.js');
|
||||
const Queue = require('denque');
|
||||
const BaseConnection = require('./connection.js');
|
||||
const Errors = require('../constants/errors.js');
|
||||
|
||||
// Source: https://github.com/go-sql-driver/mysql/blob/76c00e35a8d48f8f70f0e7dffe584692bd3fa612/packets.go#L598-L613
|
||||
function isReadOnlyError(err) {
|
||||
if (!err || !err.errno) {
|
||||
return false;
|
||||
}
|
||||
// 1792: ER_CANT_EXECUTE_IN_READ_ONLY_TRANSACTION
|
||||
// 1290: ER_OPTION_PREVENTS_STATEMENT (returned by Aurora during failover)
|
||||
// 1836: ER_READ_ONLY_MODE
|
||||
return (
|
||||
err.errno === Errors.ER_OPTION_PREVENTS_STATEMENT ||
|
||||
err.errno === Errors.ER_CANT_EXECUTE_IN_READ_ONLY_TRANSACTION ||
|
||||
err.errno === Errors.ER_READ_ONLY_MODE
|
||||
);
|
||||
}
|
||||
|
||||
function spliceConnection(queue, connection) {
|
||||
const len = queue.length;
|
||||
@@ -92,6 +108,12 @@ class BasePool extends EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
[Symbol.dispose]() {
|
||||
if (!this._closed) {
|
||||
this.end();
|
||||
}
|
||||
}
|
||||
|
||||
end(cb) {
|
||||
this._closed = true;
|
||||
clearTimeout(this._removeIdleTimeoutConnectionsTimer);
|
||||
@@ -146,8 +168,24 @@ class BasePool extends EventEmitter {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
let queryError = null;
|
||||
const origOnResult = cmdQuery.onResult;
|
||||
if (origOnResult) {
|
||||
cmdQuery.onResult = function (err, rows, fields) {
|
||||
queryError = err || null;
|
||||
origOnResult(err, rows, fields);
|
||||
};
|
||||
} else {
|
||||
cmdQuery.once('error', (err) => {
|
||||
queryError = err;
|
||||
});
|
||||
}
|
||||
conn.query(cmdQuery).once('end', () => {
|
||||
conn.release();
|
||||
if (isReadOnlyError(queryError)) {
|
||||
conn.destroy();
|
||||
} else {
|
||||
conn.release();
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
conn.release();
|
||||
@@ -169,9 +207,16 @@ class BasePool extends EventEmitter {
|
||||
return cb(err);
|
||||
}
|
||||
try {
|
||||
conn.execute(sql, values, cb).once('end', () => {
|
||||
conn.release();
|
||||
});
|
||||
conn
|
||||
.execute(sql, values, (err, rows, fields) => {
|
||||
if (isReadOnlyError(err)) {
|
||||
conn.destroy();
|
||||
}
|
||||
cb(err, rows, fields);
|
||||
})
|
||||
.once('end', () => {
|
||||
conn.release();
|
||||
});
|
||||
} catch (e) {
|
||||
conn.release();
|
||||
return cb(e);
|
||||
|
||||
11
node_modules/mysql2/lib/base/pool_connection.js
generated
vendored
11
node_modules/mysql2/lib/base/pool_connection.js
generated
vendored
@@ -29,10 +29,14 @@ class BasePoolConnection extends BaseConnection {
|
||||
this._pool.releaseConnection(this);
|
||||
}
|
||||
|
||||
end() {
|
||||
[Symbol.dispose]() {
|
||||
this.release();
|
||||
}
|
||||
|
||||
end(callback) {
|
||||
if (this.config.gracefulEnd) {
|
||||
this._removeFromPool();
|
||||
super.end();
|
||||
super.end(callback);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -46,6 +50,9 @@ class BasePoolConnection extends BaseConnection {
|
||||
this.emit('warn', err);
|
||||
console.warn(err.message);
|
||||
this.release();
|
||||
if (typeof callback === 'function') {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
|
||||
destroy() {
|
||||
|
||||
32
node_modules/mysql2/lib/commands/auth_switch.js
generated
vendored
32
node_modules/mysql2/lib/commands/auth_switch.js
generated
vendored
@@ -11,12 +11,31 @@ const caching_sha2_password = require('../auth_plugins/caching_sha2_password.js'
|
||||
const mysql_native_password = require('../auth_plugins/mysql_native_password.js');
|
||||
const mysql_clear_password = require('../auth_plugins/mysql_clear_password.js');
|
||||
|
||||
const standardAuthPlugins = {
|
||||
// Use Object.create(null) to avoid prototype pollution
|
||||
// This prevents server-controlled pluginName values like "toString" or "__proto__"
|
||||
// from resolving to prototype properties
|
||||
const standardAuthPlugins = Object.assign(Object.create(null), {
|
||||
sha256_password: sha256_password({}),
|
||||
caching_sha2_password: caching_sha2_password({}),
|
||||
mysql_native_password: mysql_native_password({}),
|
||||
mysql_clear_password: mysql_clear_password({}),
|
||||
};
|
||||
});
|
||||
|
||||
// Helper function to get auth plugin (custom or standard)
|
||||
function getAuthPlugin(pluginName, connection) {
|
||||
const customPlugins = connection.config.authPlugins;
|
||||
|
||||
// Check custom plugins with hasOwnProperty for safety
|
||||
if (
|
||||
customPlugins &&
|
||||
Object.prototype.hasOwnProperty.call(customPlugins, pluginName)
|
||||
) {
|
||||
return customPlugins[pluginName];
|
||||
}
|
||||
|
||||
// Safe to access standardAuthPlugins directly since it has no prototype
|
||||
return standardAuthPlugins[pluginName];
|
||||
}
|
||||
|
||||
function warnLegacyAuthSwitch() {
|
||||
console.warn(
|
||||
@@ -35,8 +54,6 @@ function authSwitchPluginError(error, command) {
|
||||
function authSwitchRequest(packet, connection, command) {
|
||||
const { pluginName, pluginData } =
|
||||
Packets.AuthSwitchRequest.fromPacket(packet);
|
||||
let authPlugin =
|
||||
connection.config.authPlugins && connection.config.authPlugins[pluginName];
|
||||
|
||||
// legacy plugin api don't allow to override mysql_native_password
|
||||
// if pluginName is mysql_native_password it's using standard auth4.1 auth
|
||||
@@ -54,9 +71,8 @@ function authSwitchRequest(packet, connection, command) {
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (!authPlugin) {
|
||||
authPlugin = standardAuthPlugins[pluginName];
|
||||
}
|
||||
|
||||
const authPlugin = getAuthPlugin(pluginName, connection);
|
||||
if (!authPlugin) {
|
||||
throw new Error(
|
||||
`Server requests authentication using unknown plugin ${pluginName}. See ${'TODO: add plugins doco here'} on how to configure or author authentication plugins.`
|
||||
@@ -108,4 +124,6 @@ function authSwitchRequestMoreData(packet, connection, command) {
|
||||
module.exports = {
|
||||
authSwitchRequest,
|
||||
authSwitchRequestMoreData,
|
||||
getAuthPlugin,
|
||||
standardAuthPlugins,
|
||||
};
|
||||
|
||||
144
node_modules/mysql2/lib/commands/client_handshake.js
generated
vendored
144
node_modules/mysql2/lib/commands/client_handshake.js
generated
vendored
@@ -15,6 +15,10 @@ const Packets = require('../packets/index.js');
|
||||
const ClientConstants = require('../constants/client.js');
|
||||
const CharsetToEncoding = require('../constants/charset_encodings.js');
|
||||
const auth41 = require('../auth_41.js');
|
||||
const { getAuthPlugin } = require('./auth_switch.js');
|
||||
const {
|
||||
calculateToken: calculateSha2Token,
|
||||
} = require('../auth_plugins/caching_sha2_password.js');
|
||||
|
||||
function flagNames(flags) {
|
||||
const res = [];
|
||||
@@ -67,6 +71,61 @@ class ClientHandshake extends Command {
|
||||
this.passwordSha1 = connection.config.passwordSha1;
|
||||
this.database = connection.config.database;
|
||||
this.authPluginName = this.handshake.authPluginName;
|
||||
|
||||
// Optimization: Try to use the server's preferred authentication method
|
||||
// to avoid an unnecessary auth switch roundtrip
|
||||
const serverAuthMethod = this.handshake.authPluginName;
|
||||
const isSecureConnection =
|
||||
connection.config.ssl || connection.config.socketPath;
|
||||
|
||||
// Combine auth plugin data for easier handling
|
||||
// Note: authPluginData2 can include a trailing NUL byte when PLUGIN_AUTH is set
|
||||
// We must ensure exactly 20 bytes for the scramble
|
||||
const authPluginData =
|
||||
this.handshake.authPluginData1 && this.handshake.authPluginData2
|
||||
? Buffer.concat([
|
||||
this.handshake.authPluginData1,
|
||||
this.handshake.authPluginData2,
|
||||
]).slice(0, 20)
|
||||
: Buffer.alloc(20);
|
||||
|
||||
// Check if user has custom auth plugin or legacy handler for the server-advertised method
|
||||
// If so, we must not bypass the auth switch flow with our built-in implementation
|
||||
const hasCustomAuthPlugin =
|
||||
connection.config.authPlugins &&
|
||||
Object.prototype.hasOwnProperty.call(
|
||||
connection.config.authPlugins,
|
||||
serverAuthMethod
|
||||
);
|
||||
const hasLegacyAuthSwitchHandler =
|
||||
typeof connection.config.authSwitchHandler === 'function';
|
||||
|
||||
// Determine which auth method to use
|
||||
// Try to use server's preferred method if we can, otherwise fallback to native
|
||||
const canUseDirectAuth =
|
||||
!hasCustomAuthPlugin &&
|
||||
!hasLegacyAuthSwitchHandler &&
|
||||
this.canUseAuthMethodDirectly(serverAuthMethod, isSecureConnection);
|
||||
|
||||
const clientAuthMethod = canUseDirectAuth
|
||||
? serverAuthMethod
|
||||
: 'mysql_native_password';
|
||||
|
||||
// Calculate the auth token for the chosen method
|
||||
const authToken = this.calculateAuthToken(
|
||||
clientAuthMethod,
|
||||
this.password,
|
||||
authPluginData
|
||||
);
|
||||
|
||||
if (connection.config.debug) {
|
||||
console.log(
|
||||
'Server auth method: %s, Using auth method: %s',
|
||||
serverAuthMethod,
|
||||
clientAuthMethod
|
||||
);
|
||||
}
|
||||
|
||||
const handshakeResponse = new Packets.HandshakeResponse({
|
||||
flags: this.clientFlags,
|
||||
user: this.user,
|
||||
@@ -78,8 +137,17 @@ class ClientHandshake extends Command {
|
||||
authPluginData2: this.handshake.authPluginData2,
|
||||
compress: connection.config.compress,
|
||||
connectAttributes: connection.config.connectAttributes,
|
||||
authToken: authToken,
|
||||
authPluginName: clientAuthMethod,
|
||||
});
|
||||
connection.writePacket(handshakeResponse.toPacket());
|
||||
|
||||
// If we used a non-native auth method in the initial handshake response,
|
||||
// we need to prepare for potential AuthMoreData packets by creating
|
||||
// the appropriate auth plugin instance
|
||||
if (clientAuthMethod !== 'mysql_native_password') {
|
||||
this.initializeAuthPlugin(clientAuthMethod, authPluginData, connection);
|
||||
}
|
||||
}
|
||||
|
||||
calculateNativePasswordAuthToken(authPluginData) {
|
||||
@@ -103,6 +171,82 @@ class ClientHandshake extends Command {
|
||||
return authToken;
|
||||
}
|
||||
|
||||
calculateSha256Token(password, scramble) {
|
||||
// Reuse the token calculation from caching_sha2_password plugin
|
||||
// to avoid code duplication and ensure consistency
|
||||
return calculateSha2Token(password, scramble);
|
||||
}
|
||||
|
||||
// Helper: Calculate auth token for a specific auth method
|
||||
calculateAuthToken(authMethod, password, authPluginData) {
|
||||
switch (authMethod) {
|
||||
case 'mysql_native_password':
|
||||
return this.calculateNativePasswordAuthToken(authPluginData);
|
||||
|
||||
case 'caching_sha2_password':
|
||||
return this.calculateSha256Token(password, authPluginData);
|
||||
|
||||
case 'sha256_password':
|
||||
case 'mysql_clear_password':
|
||||
// These methods send plaintext password over secure connections
|
||||
return password
|
||||
? Buffer.from(`${password}\0`, 'utf8')
|
||||
: Buffer.alloc(0);
|
||||
|
||||
default:
|
||||
// Unknown method - use native password as fallback
|
||||
return this.calculateNativePasswordAuthToken(authPluginData);
|
||||
}
|
||||
}
|
||||
|
||||
// Helper: Determine if we can use a specific auth method directly
|
||||
canUseAuthMethodDirectly(authMethod, isSecureConnection) {
|
||||
switch (authMethod) {
|
||||
case 'mysql_native_password':
|
||||
case 'caching_sha2_password':
|
||||
// These methods work with or without SSL
|
||||
return true;
|
||||
|
||||
case 'sha256_password':
|
||||
case 'mysql_clear_password':
|
||||
// These methods require secure connection for direct use
|
||||
return isSecureConnection;
|
||||
|
||||
default:
|
||||
// Unknown methods - fallback to native password
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Helper: Initialize auth plugin for handling subsequent AuthMoreData packets
|
||||
initializeAuthPlugin(authMethod, authPluginData, connection) {
|
||||
const authPlugin = getAuthPlugin(authMethod, connection);
|
||||
if (!authPlugin) {
|
||||
return; // Plugin not found, will fallback to auth switch if needed
|
||||
}
|
||||
|
||||
// Initialize the plugin with connection and command context
|
||||
const pluginHandler = authPlugin({ connection, command: this });
|
||||
connection._authPlugin = pluginHandler;
|
||||
|
||||
// Prime the plugin by calling it with the scramble data
|
||||
// This advances the plugin's state machine (e.g., to STATE_TOKEN_SENT)
|
||||
// We don't send the result because we already included it in the handshake response
|
||||
try {
|
||||
Promise.resolve(pluginHandler(authPluginData)).catch((err) => {
|
||||
// Ignore errors during initialization since we already sent the token
|
||||
if (connection.config.debug) {
|
||||
console.log('Auth plugin initialization:', err.message);
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
// Ignore synchronous errors during initialization
|
||||
if (connection.config.debug) {
|
||||
console.log('Auth plugin initialization error:', err.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
handshakeInit(helloPacket, connection) {
|
||||
this.on('error', (e) => {
|
||||
connection._fatalError = e;
|
||||
|
||||
32
node_modules/mysql2/lib/compressed_protocol.js
generated
vendored
32
node_modules/mysql2/lib/compressed_protocol.js
generated
vendored
@@ -6,6 +6,32 @@
|
||||
const zlib = require('zlib');
|
||||
const PacketParser = require('./packet_parser.js');
|
||||
|
||||
class Queue {
|
||||
constructor() {
|
||||
this._queue = [];
|
||||
this._running = false;
|
||||
}
|
||||
|
||||
push(fn) {
|
||||
this._queue.push(fn);
|
||||
if (!this._running) {
|
||||
this._running = true;
|
||||
process.nextTick(() => this._next());
|
||||
}
|
||||
}
|
||||
|
||||
_next() {
|
||||
const task = this._queue.shift();
|
||||
if (!task) {
|
||||
this._running = false;
|
||||
return;
|
||||
}
|
||||
task({
|
||||
done: () => process.nextTick(() => this._next()),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function handleCompressedPacket(packet) {
|
||||
// eslint-disable-next-line consistent-this, no-invalid-this
|
||||
const connection = this;
|
||||
@@ -117,11 +143,11 @@ function enableCompression(connection) {
|
||||
connection.writeUncompressed = connection.write;
|
||||
connection.write = writeCompressed;
|
||||
|
||||
const seqqueue = require('seq-queue');
|
||||
connection.inflateQueue = seqqueue.createQueue();
|
||||
connection.deflateQueue = seqqueue.createQueue();
|
||||
connection.inflateQueue = new Queue();
|
||||
connection.deflateQueue = new Queue();
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
enableCompression: enableCompression,
|
||||
Queue: Queue,
|
||||
};
|
||||
|
||||
7
node_modules/mysql2/lib/connection_config.js
generated
vendored
7
node_modules/mysql2/lib/connection_config.js
generated
vendored
@@ -277,7 +277,10 @@ class ConnectionConfig {
|
||||
user: decodeURIComponent(parsedUrl.username),
|
||||
password: decodeURIComponent(parsedUrl.password),
|
||||
};
|
||||
parsedUrl.searchParams.forEach((value, key) => {
|
||||
for (const [key, value] of parsedUrl.searchParams) {
|
||||
if (key in options) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
// Try to parse this as a JSON expression first
|
||||
options[key] = JSON.parse(value);
|
||||
@@ -285,7 +288,7 @@ class ConnectionConfig {
|
||||
// Otherwise assume it is a plain string
|
||||
options[key] = value;
|
||||
}
|
||||
});
|
||||
}
|
||||
return options;
|
||||
}
|
||||
}
|
||||
|
||||
1
node_modules/mysql2/lib/constants/charset_encodings.js
generated
vendored
1
node_modules/mysql2/lib/constants/charset_encodings.js
generated
vendored
@@ -313,4 +313,5 @@ module.exports = [
|
||||
'utf8',
|
||||
'utf8',
|
||||
'utf8',
|
||||
'utf8',
|
||||
];
|
||||
|
||||
61
node_modules/mysql2/lib/packets/handshake_response.js
generated
vendored
61
node_modules/mysql2/lib/packets/handshake_response.js
generated
vendored
@@ -16,22 +16,47 @@ class HandshakeResponse {
|
||||
this.authPluginData2 = handshake.authPluginData2;
|
||||
this.compress = handshake.compress;
|
||||
this.clientFlags = handshake.flags;
|
||||
// TODO: pre-4.1 auth support
|
||||
let authToken;
|
||||
if (this.passwordSha1) {
|
||||
authToken = auth41.calculateTokenFromPasswordSha(
|
||||
this.passwordSha1,
|
||||
this.authPluginData1,
|
||||
this.authPluginData2
|
||||
);
|
||||
|
||||
// Accept pre-calculated authToken and authPluginName from caller
|
||||
// This allows the caller to optimize by using the server's preferred auth method
|
||||
if (
|
||||
handshake.authToken !== undefined &&
|
||||
handshake.authPluginName !== undefined
|
||||
) {
|
||||
// Validate types to fail fast with clear errors
|
||||
if (!Buffer.isBuffer(handshake.authToken)) {
|
||||
throw new TypeError(
|
||||
'HandshakeResponse authToken must be a Buffer when provided'
|
||||
);
|
||||
}
|
||||
if (typeof handshake.authPluginName !== 'string') {
|
||||
throw new TypeError(
|
||||
'HandshakeResponse authPluginName must be a string when provided'
|
||||
);
|
||||
}
|
||||
this.authToken = handshake.authToken;
|
||||
this.authPluginName = handshake.authPluginName;
|
||||
} else {
|
||||
authToken = auth41.calculateToken(
|
||||
this.password,
|
||||
this.authPluginData1,
|
||||
this.authPluginData2
|
||||
);
|
||||
// Fallback to legacy behavior: calculate mysql_native_password token
|
||||
// TODO: pre-4.1 auth support
|
||||
let authToken;
|
||||
if (this.passwordSha1) {
|
||||
authToken = auth41.calculateTokenFromPasswordSha(
|
||||
this.passwordSha1,
|
||||
this.authPluginData1,
|
||||
this.authPluginData2
|
||||
);
|
||||
} else {
|
||||
authToken = auth41.calculateToken(
|
||||
this.password,
|
||||
this.authPluginData1,
|
||||
this.authPluginData2
|
||||
);
|
||||
}
|
||||
this.authToken = authToken;
|
||||
this.authPluginName = 'mysql_native_password';
|
||||
}
|
||||
this.authToken = authToken;
|
||||
|
||||
this.charsetNumber = handshake.charsetNumber;
|
||||
this.encoding = CharsetToEncoding[handshake.charsetNumber];
|
||||
this.connectAttributes = handshake.connectAttributes;
|
||||
@@ -62,8 +87,12 @@ class HandshakeResponse {
|
||||
packet.writeNullTerminatedString(this.database, encoding);
|
||||
}
|
||||
if (isSet('PLUGIN_AUTH')) {
|
||||
// TODO: pass from config
|
||||
packet.writeNullTerminatedString('mysql_native_password', 'latin1');
|
||||
// Use the auth plugin name specified by the caller (optimized for server's preference)
|
||||
// or fall back to mysql_native_password for backward compatibility
|
||||
packet.writeNullTerminatedString(
|
||||
this.authPluginName || 'mysql_native_password',
|
||||
'latin1'
|
||||
);
|
||||
}
|
||||
if (isSet('CONNECT_ATTRS')) {
|
||||
const connectAttributes = this.connectAttributes || {};
|
||||
|
||||
82
node_modules/mysql2/lib/packets/packet.js
generated
vendored
82
node_modules/mysql2/lib/packets/packet.js
generated
vendored
@@ -406,8 +406,8 @@ class Packet {
|
||||
readNullTerminatedString(encoding) {
|
||||
const start = this.offset;
|
||||
let end = this.offset;
|
||||
while (this.buffer[end]) {
|
||||
end = end + 1; // TODO: handle OOB check
|
||||
while (end < this.end && this.buffer[end] !== 0x00) {
|
||||
end = end + 1;
|
||||
}
|
||||
this.offset = end + 1;
|
||||
return StringParser.decode(this.buffer, encoding, start, end);
|
||||
@@ -516,16 +516,20 @@ class Packet {
|
||||
return result * sign;
|
||||
}
|
||||
|
||||
// copy-paste from https://github.com/mysqljs/mysql/blob/master/lib/protocol/Parser.js
|
||||
// adapted from https://github.com/mysqljs/mysql/blob/dc9c152a87ec51a1f647447268917243d2eab1fd/lib/protocol/Parser.js
|
||||
parseGeometryValue() {
|
||||
const buffer = this.readLengthCodedBuffer();
|
||||
let offset = 4;
|
||||
if (buffer === null || !buffer.length) {
|
||||
return null;
|
||||
}
|
||||
const bufferLength = buffer.length;
|
||||
function parseGeometry() {
|
||||
let x, y, i, j, numPoints, line;
|
||||
let x, y, i, j, numPoints, numRings, num, line;
|
||||
let result = null;
|
||||
if (offset + 5 > bufferLength) {
|
||||
return null;
|
||||
}
|
||||
const byteOrder = buffer.readUInt8(offset);
|
||||
offset += 1;
|
||||
const wkbType = byteOrder
|
||||
@@ -534,6 +538,9 @@ class Packet {
|
||||
offset += 4;
|
||||
switch (wkbType) {
|
||||
case 1: // WKBPoint
|
||||
if (offset + 16 > bufferLength) {
|
||||
return null;
|
||||
}
|
||||
x = byteOrder
|
||||
? buffer.readDoubleLE(offset)
|
||||
: buffer.readDoubleBE(offset);
|
||||
@@ -545,12 +552,21 @@ class Packet {
|
||||
result = { x: x, y: y };
|
||||
break;
|
||||
case 2: // WKBLineString
|
||||
if (offset + 4 > bufferLength) {
|
||||
return null;
|
||||
}
|
||||
numPoints = byteOrder
|
||||
? buffer.readUInt32LE(offset)
|
||||
: buffer.readUInt32BE(offset);
|
||||
offset += 4;
|
||||
if (numPoints > (bufferLength - offset) / 16) {
|
||||
return null;
|
||||
}
|
||||
result = [];
|
||||
for (i = numPoints; i > 0; i--) {
|
||||
if (offset + 16 > bufferLength) {
|
||||
break;
|
||||
}
|
||||
x = byteOrder
|
||||
? buffer.readDoubleLE(offset)
|
||||
: buffer.readDoubleBE(offset);
|
||||
@@ -563,19 +579,30 @@ class Packet {
|
||||
}
|
||||
break;
|
||||
case 3: // WKBPolygon
|
||||
// eslint-disable-next-line no-case-declarations
|
||||
const numRings = byteOrder
|
||||
if (offset + 4 > bufferLength) {
|
||||
return null;
|
||||
}
|
||||
numRings = byteOrder
|
||||
? buffer.readUInt32LE(offset)
|
||||
: buffer.readUInt32BE(offset);
|
||||
offset += 4;
|
||||
if (numRings > (bufferLength - offset) / 4) {
|
||||
return null;
|
||||
}
|
||||
result = [];
|
||||
for (i = numRings; i > 0; i--) {
|
||||
if (offset + 4 > bufferLength) {
|
||||
break;
|
||||
}
|
||||
numPoints = byteOrder
|
||||
? buffer.readUInt32LE(offset)
|
||||
: buffer.readUInt32BE(offset);
|
||||
offset += 4;
|
||||
line = [];
|
||||
for (j = numPoints; j > 0; j--) {
|
||||
if (offset + 16 > bufferLength) {
|
||||
break;
|
||||
}
|
||||
x = byteOrder
|
||||
? buffer.readDoubleLE(offset)
|
||||
: buffer.readDoubleBE(offset);
|
||||
@@ -593,11 +620,16 @@ class Packet {
|
||||
case 5: // WKBMultiLineString
|
||||
case 6: // WKBMultiPolygon
|
||||
case 7: // WKBGeometryCollection
|
||||
// eslint-disable-next-line no-case-declarations
|
||||
const num = byteOrder
|
||||
if (offset + 4 > bufferLength) {
|
||||
return null;
|
||||
}
|
||||
num = byteOrder
|
||||
? buffer.readUInt32LE(offset)
|
||||
: buffer.readUInt32BE(offset);
|
||||
offset += 4;
|
||||
if (num > (bufferLength - offset) / 9) {
|
||||
return null;
|
||||
}
|
||||
result = [];
|
||||
for (i = num; i > 0; i--) {
|
||||
result.push(parseGeometry());
|
||||
@@ -660,14 +692,27 @@ class Packet {
|
||||
if (len === null) {
|
||||
return null;
|
||||
}
|
||||
if (len === 0) {
|
||||
return 0; // TODO: assert? exception?
|
||||
}
|
||||
|
||||
// For numbers with many digits (>17), use built-in parseFloat to avoid
|
||||
// precision loss from accumulated rounding errors in repeated *10 operations.
|
||||
// This fixes issues #2928 (MAX_VALUE doubles) and #3690 (DECIMAL(36,18))
|
||||
// where very large numbers or numbers with many fractional digits lose precision.
|
||||
// The threshold of 17 is based on IEEE 754 double precision (~15-17 significant digits).
|
||||
// Testing shows minimal performance impact as most real-world numbers are shorter.
|
||||
if (len > 17) {
|
||||
const str = this.buffer.toString('utf8', this.offset, this.offset + len);
|
||||
this.offset += len;
|
||||
return Number.parseFloat(str);
|
||||
}
|
||||
|
||||
let result = 0;
|
||||
const end = this.offset + len;
|
||||
let factor = 1;
|
||||
let pastDot = false;
|
||||
let charCode = 0;
|
||||
if (len === 0) {
|
||||
return 0; // TODO: assert? exception?
|
||||
}
|
||||
if (this.buffer[this.offset] === minus) {
|
||||
this.offset++;
|
||||
factor = -1;
|
||||
@@ -681,9 +726,13 @@ class Packet {
|
||||
pastDot = true;
|
||||
this.offset++;
|
||||
} else if (charCode === exponent || charCode === exponentCapital) {
|
||||
this.offset++;
|
||||
const exponentValue = this.parseInt(end - this.offset);
|
||||
return (result / factor) * Math.pow(10, exponentValue);
|
||||
// Scientific notation detected - bail out to parseFloat for exact match.
|
||||
// Manual calculation with Math.pow(10, exp) cannot match parseFloat()
|
||||
// exactly for most non-zero exponents due to accumulated rounding errors.
|
||||
const start = end - len;
|
||||
const str = this.buffer.toString('utf8', start, end);
|
||||
this.offset = end;
|
||||
return Number.parseFloat(str);
|
||||
} else {
|
||||
result *= 10;
|
||||
result += this.buffer[this.offset] - 48;
|
||||
@@ -831,11 +880,10 @@ class Packet {
|
||||
if (n === null) {
|
||||
return this.writeInt8(0xfb);
|
||||
}
|
||||
// TODO: check that n is out of int precision
|
||||
this.writeInt8(0xfe);
|
||||
this.buffer.writeUInt32LE(n, this.offset);
|
||||
this.buffer.writeUInt32LE(n >>> 0, this.offset);
|
||||
this.offset += 4;
|
||||
this.buffer.writeUInt32LE(n >> 32, this.offset);
|
||||
this.buffer.writeUInt32LE(Math.floor(n / 0x100000000), this.offset);
|
||||
this.offset += 4;
|
||||
return this.offset;
|
||||
}
|
||||
|
||||
6
node_modules/mysql2/lib/pool_cluster.js
generated
vendored
6
node_modules/mysql2/lib/pool_cluster.js
generated
vendored
@@ -229,6 +229,12 @@ class PoolCluster extends EventEmitter {
|
||||
namespace.getConnection(cb);
|
||||
}
|
||||
|
||||
[Symbol.dispose]() {
|
||||
if (!this._closed) {
|
||||
this.end();
|
||||
}
|
||||
}
|
||||
|
||||
end(callback) {
|
||||
const cb =
|
||||
callback !== undefined
|
||||
|
||||
6
node_modules/mysql2/lib/promise/connection.js
generated
vendored
6
node_modules/mysql2/lib/promise/connection.js
generated
vendored
@@ -66,6 +66,12 @@ class PromiseConnection extends EventEmitter {
|
||||
});
|
||||
}
|
||||
|
||||
async [Symbol.asyncDispose]() {
|
||||
if (!this.connection._closing) {
|
||||
await this.end();
|
||||
}
|
||||
}
|
||||
|
||||
beginTransaction() {
|
||||
const c = this.connection;
|
||||
const localErr = new Error();
|
||||
|
||||
6
node_modules/mysql2/lib/promise/pool.js
generated
vendored
6
node_modules/mysql2/lib/promise/pool.js
generated
vendored
@@ -85,6 +85,12 @@ class PromisePool extends EventEmitter {
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async [Symbol.asyncDispose]() {
|
||||
if (!this.pool._closed) {
|
||||
await this.end();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(function (functionsToWrap) {
|
||||
|
||||
4
node_modules/mysql2/lib/promise/pool_connection.js
generated
vendored
4
node_modules/mysql2/lib/promise/pool_connection.js
generated
vendored
@@ -14,6 +14,10 @@ class PromisePoolConnection extends PromiseConnection {
|
||||
arguments
|
||||
);
|
||||
}
|
||||
|
||||
async [Symbol.asyncDispose]() {
|
||||
this.release();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = PromisePoolConnection;
|
||||
|
||||
Reference in New Issue
Block a user