Comparing version 2.16.0 to 2.17.0
@@ -7,2 +7,14 @@ # Changes | ||
## v2.17.0 (2019-04-17) | ||
* Add reverse type lookup for small performance gain #2170 | ||
* Fix `connection.threadId` missing on handshake failure | ||
* Fix duplicate packet name in debug output | ||
* Fix no password support for old password protocol | ||
* Remove special case for handshake in determine packet code | ||
* Small performance improvement starting command sequence | ||
* Support auth switch in change user flow #1776 | ||
* Support Node.js 11.x | ||
* Update `bignumber.js` to 6.0.0 | ||
## v2.16.0 (2018-07-17) | ||
@@ -9,0 +21,0 @@ |
@@ -103,2 +103,3 @@ var Crypto = require('crypto'); | ||
this._protocol.on('handshake', this._handleProtocolHandshake.bind(this)); | ||
this._protocol.on('initialize', this._handleProtocolInitialize.bind(this)); | ||
this._protocol.on('unhandledError', this._handleProtocolError.bind(this)); | ||
@@ -280,48 +281,48 @@ this._protocol.on('drain', this._handleProtocolDrain.bind(this)); | ||
Connection.prototype._startTLS = function _startTLS(onSecure) { | ||
var connection = this; | ||
var secureContext = tls.createSecureContext({ | ||
ca : this.config.ssl.ca, | ||
cert : this.config.ssl.cert, | ||
ciphers : this.config.ssl.ciphers, | ||
key : this.config.ssl.key, | ||
passphrase : this.config.ssl.passphrase | ||
}); | ||
var connection = this; | ||
// "unpipe" | ||
this._socket.removeAllListeners('data'); | ||
this._protocol.removeAllListeners('data'); | ||
// socket <-> encrypted | ||
var rejectUnauthorized = this.config.ssl.rejectUnauthorized; | ||
var secureEstablished = false; | ||
var secureSocket = new tls.TLSSocket(this._socket, { | ||
rejectUnauthorized : rejectUnauthorized, | ||
requestCert : true, | ||
secureContext : secureContext, | ||
isServer : false | ||
}); | ||
// error handler for secure socket | ||
secureSocket.on('_tlsError', function(err) { | ||
if (secureEstablished) { | ||
connection._handleNetworkError(err); | ||
} else { | ||
createSecureContext(this.config, function (err, secureContext) { | ||
if (err) { | ||
onSecure(err); | ||
return; | ||
} | ||
}); | ||
// cleartext <-> protocol | ||
secureSocket.pipe(this._protocol); | ||
this._protocol.on('data', function(data) { | ||
secureSocket.write(data); | ||
}); | ||
// "unpipe" | ||
connection._socket.removeAllListeners('data'); | ||
connection._protocol.removeAllListeners('data'); | ||
secureSocket.on('secure', function() { | ||
secureEstablished = true; | ||
// socket <-> encrypted | ||
var rejectUnauthorized = connection.config.ssl.rejectUnauthorized; | ||
var secureEstablished = false; | ||
var secureSocket = new tls.TLSSocket(connection._socket, { | ||
rejectUnauthorized : rejectUnauthorized, | ||
requestCert : true, | ||
secureContext : secureContext, | ||
isServer : false | ||
}); | ||
onSecure(rejectUnauthorized ? this.ssl.verifyError() : null); | ||
// error handler for secure socket | ||
secureSocket.on('_tlsError', function(err) { | ||
if (secureEstablished) { | ||
connection._handleNetworkError(err); | ||
} else { | ||
onSecure(err); | ||
} | ||
}); | ||
// cleartext <-> protocol | ||
secureSocket.pipe(connection._protocol); | ||
connection._protocol.on('data', function(data) { | ||
secureSocket.write(data); | ||
}); | ||
secureSocket.on('secure', function() { | ||
secureEstablished = true; | ||
onSecure(rejectUnauthorized ? this.ssl.verifyError() : null); | ||
}); | ||
// start TLS communications | ||
secureSocket._start(); | ||
}); | ||
// start TLS communications | ||
secureSocket._start(); | ||
}; | ||
@@ -439,4 +440,7 @@ } else { | ||
Connection.prototype._handleProtocolHandshake = function _handleProtocolHandshake(packet) { | ||
this.state = 'authenticated'; | ||
Connection.prototype._handleProtocolHandshake = function _handleProtocolHandshake() { | ||
this.state = 'authenticated'; | ||
}; | ||
Connection.prototype._handleProtocolInitialize = function _handleProtocolInitialize(packet) { | ||
this.threadId = packet.threadId; | ||
@@ -460,2 +464,21 @@ }; | ||
function createSecureContext (config, cb) { | ||
var context = null; | ||
var error = null; | ||
try { | ||
context = tls.createSecureContext({ | ||
ca : config.ssl.ca, | ||
cert : config.ssl.cert, | ||
ciphers : config.ssl.ciphers, | ||
key : config.ssl.key, | ||
passphrase : config.ssl.passphrase | ||
}); | ||
} catch (err) { | ||
error = err; | ||
} | ||
cb(error, context); | ||
} | ||
function unwrapFromDomain(fn) { | ||
@@ -462,0 +485,0 @@ return function () { |
@@ -5,2 +5,14 @@ var Buffer = require('safe-buffer').Buffer; | ||
function auth(name, data, options) { | ||
options = options || {}; | ||
switch (name) { | ||
case 'mysql_native_password': | ||
return Auth.token(options.password, data.slice(0, 20)); | ||
default: | ||
return undefined; | ||
} | ||
} | ||
Auth.auth = auth; | ||
function sha1(msg) { | ||
@@ -90,2 +102,6 @@ var hash = Crypto.createHash('sha1'); | ||
Auth.scramble323 = function(message, password) { | ||
if (!password) { | ||
return Buffer.alloc(0); | ||
} | ||
var to = Buffer.allocUnsafe(8); | ||
@@ -92,0 +108,0 @@ var hashPass = this.hashPassword(password); |
@@ -1,33 +0,72 @@ | ||
// Manually extracted from mysql-5.7.9/include/mysql.h.pp | ||
// some more info here: http://dev.mysql.com/doc/refman/5.5/en/c-api-prepared-statement-type-codes.html | ||
exports.DECIMAL = 0x00; // aka DECIMAL (http://dev.mysql.com/doc/refman/5.0/en/precision-math-decimal-changes.html) | ||
exports.TINY = 0x01; // aka TINYINT, 1 byte | ||
exports.SHORT = 0x02; // aka SMALLINT, 2 bytes | ||
exports.LONG = 0x03; // aka INT, 4 bytes | ||
exports.FLOAT = 0x04; // aka FLOAT, 4-8 bytes | ||
exports.DOUBLE = 0x05; // aka DOUBLE, 8 bytes | ||
exports.NULL = 0x06; // NULL (used for prepared statements, I think) | ||
exports.TIMESTAMP = 0x07; // aka TIMESTAMP | ||
exports.LONGLONG = 0x08; // aka BIGINT, 8 bytes | ||
exports.INT24 = 0x09; // aka MEDIUMINT, 3 bytes | ||
exports.DATE = 0x0a; // aka DATE | ||
exports.TIME = 0x0b; // aka TIME | ||
exports.DATETIME = 0x0c; // aka DATETIME | ||
exports.YEAR = 0x0d; // aka YEAR, 1 byte (don't ask) | ||
exports.NEWDATE = 0x0e; // aka ? | ||
exports.VARCHAR = 0x0f; // aka VARCHAR (?) | ||
exports.BIT = 0x10; // aka BIT, 1-8 byte | ||
exports.TIMESTAMP2 = 0x11; // aka TIMESTAMP with fractional seconds | ||
exports.DATETIME2 = 0x12; // aka DATETIME with fractional seconds | ||
exports.TIME2 = 0x13; // aka TIME with fractional seconds | ||
exports.JSON = 0xf5; // aka JSON | ||
exports.NEWDECIMAL = 0xf6; // aka DECIMAL | ||
exports.ENUM = 0xf7; // aka ENUM | ||
exports.SET = 0xf8; // aka SET | ||
exports.TINY_BLOB = 0xf9; // aka TINYBLOB, TINYTEXT | ||
exports.MEDIUM_BLOB = 0xfa; // aka MEDIUMBLOB, MEDIUMTEXT | ||
exports.LONG_BLOB = 0xfb; // aka LONGBLOG, LONGTEXT | ||
exports.BLOB = 0xfc; // aka BLOB, TEXT | ||
exports.VAR_STRING = 0xfd; // aka VARCHAR, VARBINARY | ||
exports.STRING = 0xfe; // aka CHAR, BINARY | ||
exports.GEOMETRY = 0xff; // aka GEOMETRY | ||
/** | ||
* MySQL type constants | ||
* | ||
* Extracted from version 5.7.19 | ||
* | ||
* !! Generated by generate-type-constants.js, do not modify by hand !! | ||
*/ | ||
exports.DECIMAL = 0; | ||
exports.TINY = 1; | ||
exports.SHORT = 2; | ||
exports.LONG = 3; | ||
exports.FLOAT = 4; | ||
exports.DOUBLE = 5; | ||
exports.NULL = 6; | ||
exports.TIMESTAMP = 7; | ||
exports.LONGLONG = 8; | ||
exports.INT24 = 9; | ||
exports.DATE = 10; | ||
exports.TIME = 11; | ||
exports.DATETIME = 12; | ||
exports.YEAR = 13; | ||
exports.NEWDATE = 14; | ||
exports.VARCHAR = 15; | ||
exports.BIT = 16; | ||
exports.TIMESTAMP2 = 17; | ||
exports.DATETIME2 = 18; | ||
exports.TIME2 = 19; | ||
exports.JSON = 245; | ||
exports.NEWDECIMAL = 246; | ||
exports.ENUM = 247; | ||
exports.SET = 248; | ||
exports.TINY_BLOB = 249; | ||
exports.MEDIUM_BLOB = 250; | ||
exports.LONG_BLOB = 251; | ||
exports.BLOB = 252; | ||
exports.VAR_STRING = 253; | ||
exports.STRING = 254; | ||
exports.GEOMETRY = 255; | ||
// Lookup-by-number table | ||
exports[0] = 'DECIMAL'; | ||
exports[1] = 'TINY'; | ||
exports[2] = 'SHORT'; | ||
exports[3] = 'LONG'; | ||
exports[4] = 'FLOAT'; | ||
exports[5] = 'DOUBLE'; | ||
exports[6] = 'NULL'; | ||
exports[7] = 'TIMESTAMP'; | ||
exports[8] = 'LONGLONG'; | ||
exports[9] = 'INT24'; | ||
exports[10] = 'DATE'; | ||
exports[11] = 'TIME'; | ||
exports[12] = 'DATETIME'; | ||
exports[13] = 'YEAR'; | ||
exports[14] = 'NEWDATE'; | ||
exports[15] = 'VARCHAR'; | ||
exports[16] = 'BIT'; | ||
exports[17] = 'TIMESTAMP2'; | ||
exports[18] = 'DATETIME2'; | ||
exports[19] = 'TIME2'; | ||
exports[245] = 'JSON'; | ||
exports[246] = 'NEWDECIMAL'; | ||
exports[247] = 'ENUM'; | ||
exports[248] = 'SET'; | ||
exports[249] = 'TINY_BLOB'; | ||
exports[250] = 'MEDIUM_BLOB'; | ||
exports[251] = 'LONG_BLOB'; | ||
exports[252] = 'BLOB'; | ||
exports[253] = 'VAR_STRING'; | ||
exports[254] = 'STRING'; | ||
exports[255] = 'GEOMETRY'; |
@@ -12,3 +12,3 @@ var Types = require('../constants/types'); | ||
this.name = options.packet.name; | ||
this.type = typeToString(options.packet.type); | ||
this.type = Types[options.packet.type]; | ||
this.length = options.packet.length; | ||
@@ -28,9 +28,1 @@ } | ||
}; | ||
function typeToString(t) { | ||
for (var k in Types) { | ||
if (Types[k] === t) return k; | ||
} | ||
return undefined; | ||
} |
@@ -9,3 +9,3 @@ module.exports = OldPasswordPacket; | ||
OldPasswordPacket.prototype.parse = function(parser) { | ||
this.scrambleBuff = parser.parseNullTerminatedBuffer(); | ||
this.scrambleBuff = parser.parsePacketTerminatedBuffer(); | ||
}; | ||
@@ -15,3 +15,2 @@ | ||
writer.writeBuffer(this.scrambleBuff); | ||
writer.writeFiller(1); | ||
}; |
@@ -126,6 +126,3 @@ var Types = require('../constants/types'); | ||
if (Array.isArray(list)) { | ||
for (var i = 0; i < list.length; i++) { | ||
if (Types[list[i]] === type) return true; | ||
} | ||
return false; | ||
return list.indexOf(Types[type]) !== -1; | ||
} else { | ||
@@ -132,0 +129,0 @@ return Boolean(list); |
@@ -1,8 +0,10 @@ | ||
var MAX_PACKET_LENGTH = Math.pow(2, 24) - 1; | ||
var MUL_32BIT = Math.pow(2, 32); | ||
var PacketHeader = require('./PacketHeader'); | ||
var BigNumber = require('bignumber.js'); | ||
var Buffer = require('safe-buffer').Buffer; | ||
var BufferList = require('./BufferList'); | ||
var PacketHeader = require('./PacketHeader'); | ||
var BigNumber = require('bignumber.js'); | ||
var Buffer = require('safe-buffer').Buffer; | ||
var BufferList = require('./BufferList'); | ||
var MAX_PACKET_LENGTH = Math.pow(2, 24) - 1; | ||
var MUL_32BIT = Math.pow(2, 32); | ||
var PACKET_HEADER_LENGTH = 4; | ||
module.exports = Parser; | ||
@@ -31,67 +33,13 @@ function Parser(options) { | ||
while (!this._paused) { | ||
if (!this._packetHeader) { | ||
if (!this._combineNextBuffers(4)) { | ||
break; | ||
} | ||
var packetHeader = this._tryReadPacketHeader(); | ||
this._packetHeader = new PacketHeader( | ||
this.parseUnsignedNumber(3), | ||
this.parseUnsignedNumber(1) | ||
); | ||
if (this._packetHeader.number !== this._nextPacketNumber) { | ||
var err = new Error( | ||
'Packets out of order. Got: ' + this._packetHeader.number + ' ' + | ||
'Expected: ' + this._nextPacketNumber | ||
); | ||
err.code = 'PROTOCOL_PACKETS_OUT_OF_ORDER'; | ||
err.fatal = true; | ||
this._onError(err); | ||
} | ||
this.incrementPacketNumber(); | ||
if (!packetHeader) { | ||
break; | ||
} | ||
if (!this._combineNextBuffers(this._packetHeader.length)) { | ||
if (!this._combineNextBuffers(packetHeader.length)) { | ||
break; | ||
} | ||
this._packetEnd = this._offset + this._packetHeader.length; | ||
this._packetOffset = this._offset; | ||
if (this._packetHeader.length === MAX_PACKET_LENGTH) { | ||
this._longPacketBuffers.push(this._buffer.slice(this._packetOffset, this._packetEnd)); | ||
this._advanceToNextPacket(); | ||
continue; | ||
} | ||
this._combineLongPacketBuffers(); | ||
// Try...finally to ensure exception safety. Unfortunately this is costing | ||
// us up to ~10% performance in some benchmarks. | ||
var hadException = true; | ||
try { | ||
this._onPacket(this._packetHeader); | ||
hadException = false; | ||
} catch (err) { | ||
if (!err || typeof err.code !== 'string' || err.code.substr(0, 7) !== 'PARSER_') { | ||
throw err; // Rethrow non-MySQL errors | ||
} | ||
// Pass down parser errors | ||
this._onError(err); | ||
hadException = false; | ||
} finally { | ||
this._advanceToNextPacket(); | ||
// If we had an exception, the parser while loop will be broken out | ||
// of after the finally block. So we need to make sure to re-enter it | ||
// to continue parsing any bytes that may already have been received. | ||
if (hadException) { | ||
process.nextTick(this.write.bind(this)); | ||
} | ||
} | ||
this._parsePacket(packetHeader); | ||
} | ||
@@ -256,3 +204,3 @@ }; | ||
if (high >>> 21) { | ||
value = (new BigNumber(low)).plus((new BigNumber(MUL_32BIT)).times(high)).toString(); | ||
value = BigNumber(MUL_32BIT).times(high).plus(low).toString(); | ||
@@ -473,2 +421,69 @@ if (this._supportBigNumbers) { | ||
Parser.prototype._parsePacket = function _parsePacket(packetHeader) { | ||
this._packetEnd = this._offset + packetHeader.length; | ||
this._packetOffset = this._offset; | ||
if (packetHeader.length === MAX_PACKET_LENGTH) { | ||
this._longPacketBuffers.push(this._buffer.slice(this._packetOffset, this._packetEnd)); | ||
this._advanceToNextPacket(); | ||
return; | ||
} | ||
this._combineLongPacketBuffers(); | ||
var hadException = true; | ||
try { | ||
this._onPacket(packetHeader); | ||
hadException = false; | ||
} catch (err) { | ||
if (!err || typeof err.code !== 'string' || err.code.substr(0, 7) !== 'PARSER_') { | ||
throw err; // Rethrow non-MySQL errors | ||
} | ||
// Pass down parser errors | ||
this._onError(err); | ||
hadException = false; | ||
} finally { | ||
this._advanceToNextPacket(); | ||
// If there was an exception, the parser while loop will be broken out | ||
// of after the finally block. So schedule a blank write to re-enter it | ||
// to continue parsing any bytes that may already have been received. | ||
if (hadException) { | ||
process.nextTick(this.write.bind(this)); | ||
} | ||
} | ||
}; | ||
Parser.prototype._tryReadPacketHeader = function _tryReadPacketHeader() { | ||
if (this._packetHeader) { | ||
return this._packetHeader; | ||
} | ||
if (!this._combineNextBuffers(PACKET_HEADER_LENGTH)) { | ||
return null; | ||
} | ||
this._packetHeader = new PacketHeader( | ||
this.parseUnsignedNumber(3), | ||
this.parseUnsignedNumber(1) | ||
); | ||
if (this._packetHeader.number !== this._nextPacketNumber) { | ||
var err = new Error( | ||
'Packets out of order. Got: ' + this._packetHeader.number + ' ' + | ||
'Expected: ' + this._nextPacketNumber | ||
); | ||
err.code = 'PROTOCOL_PACKETS_OUT_OF_ORDER'; | ||
err.fatal = true; | ||
this._onError(err); | ||
} | ||
this.incrementPacketNumber(); | ||
return this._packetHeader; | ||
}; | ||
Parser.prototype._advanceToNextPacket = function() { | ||
@@ -475,0 +490,0 @@ this._offset = this._packetEnd; |
@@ -159,5 +159,2 @@ var Parser = require('./Parser'); | ||
}) | ||
.on('end', function() { | ||
self._dequeue(sequence); | ||
}) | ||
.on('timeout', function() { | ||
@@ -171,4 +168,6 @@ var err = new Error(sequence.constructor.name + ' inactivity timeout'); | ||
self._delegateError(err, sequence); | ||
}) | ||
.on('start-tls', function() { | ||
}); | ||
if (sequence.constructor === Sequences.Handshake) { | ||
sequence.on('start-tls', function () { | ||
sequence._timer.active(); | ||
@@ -189,2 +188,15 @@ self._connection._startTLS(function(err) { | ||
sequence.on('end', function () { | ||
self._handshaked = true; | ||
if (!self._fatalError) { | ||
self.emit('handshake', self._handshakeInitializationPacket); | ||
} | ||
}); | ||
} | ||
sequence.on('end', function () { | ||
self._dequeue(sequence); | ||
}); | ||
if (this._queue.length === 1) { | ||
@@ -268,2 +280,3 @@ this._parser.resetPacketNumber(); | ||
this._handshakeInitializationPacket = packet; | ||
this.emit('initialize', packet); | ||
} | ||
@@ -314,8 +327,3 @@ | ||
switch (firstByte) { | ||
case 0x00: | ||
if (!this._handshaked) { | ||
this._handshaked = true; | ||
this.emit('handshake', this._handshakeInitializationPacket); | ||
} | ||
return Packets.OkPacket; | ||
case 0x00: return Packets.OkPacket; | ||
case 0xfe: return Packets.EofPacket; | ||
@@ -444,20 +452,18 @@ case 0xff: return Packets.ErrorPacket; | ||
var connection = this._connection; | ||
var headline = incoming | ||
? '<-- ' | ||
: '--> '; | ||
var direction = incoming | ||
? '<--' | ||
: '-->'; | ||
var packetName = packet.constructor.name; | ||
var threadId = connection && connection.threadId !== null | ||
? ' (' + connection.threadId + ')' | ||
: ''; | ||
if (connection && connection.threadId !== null) { | ||
headline += '(' + connection.threadId + ') '; | ||
} | ||
headline += packet.constructor.name; | ||
// check for debug packet restriction | ||
if (Array.isArray(this._config.debug) && this._config.debug.indexOf(packet.constructor.name) === -1) { | ||
if (Array.isArray(this._config.debug) && this._config.debug.indexOf(packetName) === -1) { | ||
return; | ||
} | ||
console.log(headline); | ||
console.log(packet); | ||
console.log(''); | ||
var packetPayload = Util.inspect(packet).replace(/^[^{]+/, ''); | ||
console.log('%s%s %s %s\n', direction, threadId, packetName, packetPayload); | ||
}; |
@@ -18,2 +18,10 @@ var Sequence = require('./Sequence'); | ||
ChangeUser.prototype.determinePacket = function determinePacket(firstByte) { | ||
switch (firstByte) { | ||
case 0xfe: return Packets.AuthSwitchRequestPacket; | ||
case 0xff: return Packets.ErrorPacket; | ||
default: return undefined; | ||
} | ||
}; | ||
ChangeUser.prototype.start = function(handshakeInitializationPacket) { | ||
@@ -38,2 +46,20 @@ var scrambleBuff = handshakeInitializationPacket.scrambleBuff(); | ||
ChangeUser.prototype['AuthSwitchRequestPacket'] = function (packet) { | ||
var name = packet.authMethodName; | ||
var data = Auth.auth(name, packet.authMethodData, { | ||
password: this._password | ||
}); | ||
if (data !== undefined) { | ||
this.emit('packet', new Packets.AuthSwitchResponsePacket({ | ||
data: data | ||
})); | ||
} else { | ||
var err = new Error('MySQL is requesting the ' + name + ' authentication method, which is not supported.'); | ||
err.code = 'UNSUPPORTED_AUTH_METHOD'; | ||
err.fatal = true; | ||
this.end(err); | ||
} | ||
}; | ||
ChangeUser.prototype['ErrorPacket'] = function(packet) { | ||
@@ -40,0 +66,0 @@ var err = this._packetToError(packet); |
@@ -37,16 +37,15 @@ var Sequence = require('./Sequence'); | ||
Handshake.prototype['AuthSwitchRequestPacket'] = function (packet) { | ||
if (packet.authMethodName === 'mysql_native_password') { | ||
var challenge = packet.authMethodData.slice(0, 20); | ||
var name = packet.authMethodName; | ||
var data = Auth.auth(name, packet.authMethodData, { | ||
password: this._config.password | ||
}); | ||
if (data !== undefined) { | ||
this.emit('packet', new Packets.AuthSwitchResponsePacket({ | ||
data: Auth.token(this._config.password, challenge) | ||
data: data | ||
})); | ||
} else { | ||
var err = new Error( | ||
'MySQL is requesting the ' + packet.authMethodName + ' authentication method, which is not supported.' | ||
); | ||
err.code = 'UNSUPPORTED_AUTH_METHOD'; | ||
var err = new Error('MySQL is requesting the ' + name + ' authentication method, which is not supported.'); | ||
err.code = 'UNSUPPORTED_AUTH_METHOD'; | ||
err.fatal = true; | ||
this.end(err); | ||
@@ -53,0 +52,0 @@ } |
{ | ||
"name": "mysql", | ||
"description": "A node.js driver for mysql. It is written in JavaScript, does not require compiling, and is 100% MIT licensed.", | ||
"version": "2.16.0", | ||
"version": "2.17.0", | ||
"license": "MIT", | ||
@@ -16,3 +16,3 @@ "author": "Felix Geisendörfer <felix@debuggable.com> (http://debuggable.com/)", | ||
"dependencies": { | ||
"bignumber.js": "4.1.0", | ||
"bignumber.js": "6.0.0", | ||
"readable-stream": "2.3.6", | ||
@@ -24,5 +24,4 @@ "safe-buffer": "5.1.2", | ||
"after": "0.8.2", | ||
"eslint": "4.19.1", | ||
"nyc": "10.3.2", | ||
"seedrandom": "2.4.3", | ||
"eslint": "5.15.1", | ||
"seedrandom": "3.0.1", | ||
"timezone-mock": "0.0.7", | ||
@@ -45,6 +44,6 @@ "urun": "0.0.8", | ||
"test": "node test/run.js", | ||
"test-ci": "nyc --reporter=text npm test", | ||
"test-cov": "nyc --reporter=html --reporter=text npm test", | ||
"test-ci": "node tool/install-nyc.js --nyc-optional --reporter=text -- npm test", | ||
"test-cov": "node tool/install-nyc.js --reporter=html --reporter=text -- npm test", | ||
"version": "node tool/version-changes.js && git add Changes.md" | ||
} | ||
} |
105
Readme.md
# mysql | ||
[![NPM Version][npm-image]][npm-url] | ||
[![NPM Downloads][downloads-image]][downloads-url] | ||
[![Node.js Version][node-version-image]][node-version-url] | ||
[![NPM Version][npm-version-image]][npm-url] | ||
[![NPM Downloads][npm-downloads-image]][npm-url] | ||
[![Node.js Version][node-image]][node-url] | ||
[![Linux Build][travis-image]][travis-url] | ||
@@ -224,3 +224,3 @@ [![Windows Build][appveyor-image]][appveyor-url] | ||
Number objects. This option is ignored if `supportBigNumbers` is disabled. | ||
* `dateStrings`: Force date types (TIMESTAMP, DATETIME, DATE) to be returned as strings rather then | ||
* `dateStrings`: Force date types (TIMESTAMP, DATETIME, DATE) to be returned as strings rather than | ||
inflated into JavaScript Date objects. Can be `true`/`false` or an array of type names to keep as | ||
@@ -1159,7 +1159,10 @@ strings. (Default: `false`) | ||
* `err.code`: Either a [MySQL server error][] (e.g. | ||
`'ER_ACCESS_DENIED_ERROR'`), a Node.js error (e.g. `'ECONNREFUSED'`) or an | ||
internal error (e.g. `'PROTOCOL_CONNECTION_LOST'`). | ||
* `err.code`: String, contains the MySQL server error symbol if the error is | ||
a [MySQL server error][] (e.g. `'ER_ACCESS_DENIED_ERROR'`), a Node.js error | ||
code if it is a Node.js error (e.g. `'ECONNREFUSED'`), or an internal error | ||
code (e.g. `'PROTOCOL_CONNECTION_LOST'`). | ||
* `err.errno`: Number, contains the MySQL server error number. Only populated | ||
from [MySQL server error][]. | ||
* `err.fatal`: Boolean, indicating if this error is terminal to the connection | ||
object. If the error is not from a MySQL protocol operation, this properly | ||
object. If the error is not from a MySQL protocol operation, this property | ||
will not be defined. | ||
@@ -1174,3 +1177,3 @@ * `err.sql`: String, contains the full SQL of the failed query. This can be | ||
[Error]: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error | ||
[MySQL server error]: http://dev.mysql.com/doc/refman/5.5/en/error-messages-server.html | ||
[MySQL server error]: https://dev.mysql.com/doc/refman/5.5/en/server-error-reference.html | ||
@@ -1309,34 +1312,55 @@ Fatal errors are propagated to *all* pending callbacks. In the example below, a | ||
### Custom type casting | ||
You can also pass a function and handle type casting yourself. You're given some | ||
column information like database, table and name and also type and length. If you | ||
just want to apply a custom type casting to a specific type you can do it and then | ||
fallback to the default. Here's an example of converting `TINYINT(1)` to boolean: | ||
fallback to the default. | ||
The function is provided two arguments `field` and `next` and is expected to | ||
return the value for the given field by invoking the parser functions through | ||
the `field` object. | ||
The `field` argument is a `Field` object and contains data about the field that | ||
need to be parsed. The following are some of the properties on a `Field` object: | ||
* `db` - a string of the database the field came from. | ||
* `table` - a string of the table the field came from. | ||
* `name` - a string of the field name. | ||
* `type` - a string of the field type in all caps. | ||
* `length` - a number of the field length, as given by the database. | ||
The `next` argument is a `function` that, when called, will return the default | ||
type conversaion for the given field. | ||
When getting the field data, the following helper methods are present on the | ||
`field` object: | ||
* `.string()` - parse the field into a string. | ||
* `.buffer()` - parse the field into a `Buffer`. | ||
* `.geometry()` - parse the field as a geometry value. | ||
The MySQL protocol is a text-based protocol. This means that over the wire, all | ||
field types are represented as a string, which is why only string-like functions | ||
are available on the `field` object. Based on the type information (like `INT`), | ||
the type cast should convert the string field into a different JavaScript type | ||
(like a `number`). | ||
Here's an example of converting `TINYINT(1)` to boolean: | ||
```js | ||
connection.query({ | ||
sql: '...', | ||
connection = mysql.createConnection({ | ||
typeCast: function (field, next) { | ||
if (field.type == 'TINY' && field.length == 1) { | ||
return (field.string() == '1'); // 1 = true, 0 = false | ||
if (field.type === 'TINY' && field.length === 1) { | ||
return (field.string() === '1'); // 1 = true, 0 = false | ||
} else { | ||
return next(); | ||
} | ||
return next(); | ||
} | ||
}); | ||
``` | ||
__WARNING: YOU MUST INVOKE the parser using one of these three field functions in your custom typeCast callback. They can only be called once. (see [#539](https://github.com/mysqljs/mysql/issues/539) for discussion)__ | ||
``` | ||
field.string() | ||
field.buffer() | ||
field.geometry() | ||
``` | ||
are aliases for | ||
``` | ||
parser.parseLengthCodedString() | ||
parser.parseLengthCodedBuffer() | ||
parser.parseGeometryValue() | ||
``` | ||
__You can find which field function you need to use by looking at: [RowDataPacket.prototype._typeCast](https://github.com/mysqljs/mysql/blob/master/lib/protocol/packets/RowDataPacket.js#L41)__ | ||
__WARNING: YOU MUST INVOKE the parser using one of these three field functions | ||
in your custom typeCast callback. They can only be called once.__ | ||
## Connection Flags | ||
@@ -1437,4 +1461,4 @@ | ||
An ideal report would include a clear indication of what the security issue is | ||
and how it would be exploited, ideally with an accompaning proof of concept | ||
("PoC") for collaborators to work again and validate potentional fixes against. | ||
and how it would be exploited, ideally with an accompanying proof of concept | ||
("PoC") for collaborators to work against and validate potentional fixes against. | ||
@@ -1498,13 +1522,12 @@ ## Contributing | ||
[npm-image]: https://img.shields.io/npm/v/mysql.svg | ||
[appveyor-image]: https://badgen.net/appveyor/ci/dougwilson/node-mysql/master?label=windows | ||
[appveyor-url]: https://ci.appveyor.com/project/dougwilson/node-mysql | ||
[coveralls-image]: https://badgen.net/coveralls/c/github/mysqljs/mysql/master | ||
[coveralls-url]: https://coveralls.io/r/mysqljs/mysql?branch=master | ||
[node-image]: https://badgen.net/npm/node/mysql | ||
[node-url]: https://nodejs.org/en/download | ||
[npm-downloads-image]: https://badgen.net/npm/dm/mysql | ||
[npm-url]: https://npmjs.org/package/mysql | ||
[node-version-image]: https://img.shields.io/node/v/mysql.svg | ||
[node-version-url]: https://nodejs.org/en/download/ | ||
[travis-image]: https://img.shields.io/travis/mysqljs/mysql/master.svg?label=linux | ||
[npm-version-image]: https://badgen.net/npm/v/mysql | ||
[travis-image]: https://badgen.net/travis/mysqljs/mysql/master | ||
[travis-url]: https://travis-ci.org/mysqljs/mysql | ||
[appveyor-image]: https://img.shields.io/appveyor/ci/dougwilson/node-mysql/master.svg?label=windows | ||
[appveyor-url]: https://ci.appveyor.com/project/dougwilson/node-mysql | ||
[coveralls-image]: https://img.shields.io/coveralls/mysqljs/mysql/master.svg | ||
[coveralls-url]: https://coveralls.io/r/mysqljs/mysql?branch=master | ||
[downloads-image]: https://img.shields.io/npm/dm/mysql.svg | ||
[downloads-url]: https://npmjs.org/package/mysql |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
431383
6
7243
1528
+ Addedbignumber.js@6.0.0(transitive)
- Removedbignumber.js@4.1.0(transitive)
Updatedbignumber.js@6.0.0