123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213 |
- 'use strict';
- 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),
- ' ',
- field.columnType
- );
- }
- 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;
|