mysql2
Version:
fast mysql driver. Implements core protocol, prepared statements, ssl and compression in native JS
212 lines (191 loc) • 7.5 kB
JavaScript
const FieldFlags = require('../constants/field_flags.js');
const Charsets = require('../constants/charsets.js');
const Types = require('../constants/types.js');
const helpers = require('../helpers');
const typeNames = [];
for (const t in Types) {
typeNames[Types[t]] = t;
}
function getBinaryParser(fields, _options, config) {
function readCode(field, config, options, fieldNum, packet) {
const supportBigNumbers = Boolean(
options.supportBigNumbers || config.supportBigNumbers
);
const bigNumberStrings = Boolean(
options.bigNumberStrings || config.bigNumberStrings
);
const timezone = options.timezone || config.timezone;
const dateStrings = options.dateStrings || config.dateStrings;
const unsigned = field.flags & FieldFlags.UNSIGNED;
switch (field.columnType) {
case Types.TINY:
return unsigned ? packet.readInt8() : packet.readSInt8();
case Types.SHORT:
return unsigned ? packet.readInt16() : packet.readSInt16();
case Types.LONG:
case Types.INT24: // in binary protocol int24 is encoded in 4 bytes int32
return unsigned ? packet.readInt32() : packet.readSInt32();
case Types.YEAR:
return packet.readInt16();
case Types.FLOAT:
return packet.readFloat();
case Types.DOUBLE:
return packet.readDouble();
case Types.NULL:
return null;
case Types.DATE:
case Types.DATETIME:
case Types.TIMESTAMP:
case Types.NEWDATE:
return helpers.typeMatch(field.columnType, dateStrings, Types)
? packet.readDateTimeString(
parseInt(field.decimals, 10),
null,
field.columnType
)
: packet.readDateTime(timezone);
case Types.TIME:
return packet.readTimeString();
case Types.DECIMAL:
case Types.NEWDECIMAL:
return config.decimalNumbers
? packet.parseLengthCodedFloat()
: packet.readLengthCodedString('ascii');
case Types.GEOMETRY:
return packet.parseGeometryValue();
case Types.VECTOR:
return packet.parseVector();
case Types.JSON:
// Since for JSON columns mysql always returns charset 63 (BINARY),
// we have to handle it according to JSON specs and use "utf8",
// see https://github.com/sidorares/node-mysql2/issues/409
return config.jsonStrings
? packet.readLengthCodedString('utf8')
: JSON.parse(packet.readLengthCodedString('utf8'));
case Types.LONGLONG:
if (!supportBigNumbers)
return unsigned
? packet.readInt64JSNumber()
: packet.readSInt64JSNumber();
return bigNumberStrings
? unsigned
? packet.readInt64String()
: packet.readSInt64String()
: unsigned
? packet.readInt64()
: packet.readSInt64();
default:
return field.characterSet === Charsets.BINARY
? packet.readLengthCodedBuffer()
: packet.readLengthCodedString(fields[fieldNum].encoding);
}
}
return class BinaryRow {
constructor() {}
next(packet, fields, options) {
packet.readInt8(); // status byte
const nullBitmapLength = Math.floor((fields.length + 7 + 2) / 8);
const nullBitmaskBytes = new Array(nullBitmapLength);
for (let i = 0; i < nullBitmapLength; i++) {
nullBitmaskBytes[i] = packet.readInt8();
}
const result = options.rowsAsArray ? new Array(fields.length) : {};
let currentFieldNullBit = 4;
let nullByteIndex = 0;
for (let i = 0; i < fields.length; i++) {
const field = fields[i];
const typeCast =
options.typeCast !== undefined ? options.typeCast : config.typeCast;
let value;
if (nullBitmaskBytes[nullByteIndex] & currentFieldNullBit) {
value = null;
} else if (options.typeCast === false) {
value = packet.readLengthCodedBuffer();
} else {
const next = () => readCode(field, config, options, i, packet);
value =
typeof typeCast === 'function'
? typeCast(
{
type: typeNames[field.columnType],
length: field.columnLength,
db: field.schema,
table: field.table,
name: field.name,
string: function (encoding = field.encoding) {
if (
field.columnType === Types.JSON &&
encoding === field.encoding
) {
// Since for JSON columns mysql always returns charset 63 (BINARY),
// we have to handle it according to JSON specs and use "utf8",
// see https://github.com/sidorares/node-mysql2/issues/1661
console.warn(
`typeCast: JSON column "${field.name}" is interpreted as BINARY by default, recommended to manually set utf8 encoding: \`field.string("utf8")\``
);
}
if (
[
Types.DATETIME,
Types.NEWDATE,
Types.TIMESTAMP,
Types.DATE,
].includes(field.columnType)
) {
return packet.readDateTimeString(
parseInt(field.decimals, 10)
);
}
if (field.columnType === Types.TINY) {
const unsigned = field.flags & FieldFlags.UNSIGNED;
return String(
unsigned ? packet.readInt8() : packet.readSInt8()
);
}
if (field.columnType === Types.TIME) {
return packet.readTimeString();
}
return packet.readLengthCodedString(encoding);
},
buffer: function () {
return packet.readLengthCodedBuffer();
},
geometry: function () {
return packet.parseGeometryValue();
},
},
next
)
: next();
}
if (options.rowsAsArray) {
result[i] = value;
} else if (typeof options.nestTables === 'string') {
const key = helpers.fieldEscape(
field.table + options.nestTables + field.name,
false
);
result[key] = value;
} else if (options.nestTables === true) {
const tableName = helpers.fieldEscape(field.table, false);
if (!result[tableName]) {
result[tableName] = {};
}
const fieldName = helpers.fieldEscape(field.name, false);
result[tableName][fieldName] = value;
} else {
const key = helpers.fieldEscape(field.name, false);
result[key] = value;
}
currentFieldNullBit *= 2;
if (currentFieldNullBit === 0x100) {
currentFieldNullBit = 1;
nullByteIndex++;
}
}
return result;
}
};
}
module.exports = getBinaryParser;
;