123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509 |
- 'use strict';
- const Errors = require('../misc/errors');
- const Iconv = require('iconv-lite');
- const Long = require('long');
- const moment = require('moment-timezone');
- /**
- * Object to easily parse buffer.
- *
- */
- class Packet {
- constructor(buf, pos, end) {
- this.buf = buf;
- this.pos = pos;
- this.end = end;
- }
- skip(n) {
- this.pos += n;
- }
- readGeometry(dataTypeName) {
- const geoBuf = this.readBufferLengthEncoded();
- if (geoBuf === null || geoBuf.length === 0) {
- if (dataTypeName) {
- switch (dataTypeName) {
- case 'point':
- return { type: 'Point' };
- case 'linestring':
- return { type: 'LineString' };
- case 'polygon':
- return { type: 'Polygon' };
- case 'multipoint':
- return { type: 'MultiPoint' };
- case 'multilinestring':
- return { type: 'MultiLineString' };
- case 'multipolygon':
- return { type: 'MultiPolygon' };
- default:
- return { type: dataTypeName };
- }
- }
- return null;
- }
- let geoPos = 4;
- return readGeometryObject(false);
- function parseCoordinates(byteOrder) {
- geoPos += 16;
- const x = byteOrder ? geoBuf.readDoubleLE(geoPos - 16) : geoBuf.readDoubleBE(geoPos - 16);
- const y = byteOrder ? geoBuf.readDoubleLE(geoPos - 8) : geoBuf.readDoubleBE(geoPos - 8);
- return [x, y];
- }
- function readGeometryObject(inner) {
- const byteOrder = geoBuf[geoPos++];
- const wkbType = byteOrder ? geoBuf.readInt32LE(geoPos) : geoBuf.readInt32BE(geoPos);
- geoPos += 4;
- switch (wkbType) {
- case 1: //wkbPoint
- const coords = parseCoordinates(byteOrder);
- if (inner) return coords;
- return {
- type: 'Point',
- coordinates: coords
- };
- case 2: //wkbLineString
- const pointNumber = byteOrder ? geoBuf.readInt32LE(geoPos) : geoBuf.readInt32BE(geoPos);
- geoPos += 4;
- let coordinates = [];
- for (let i = 0; i < pointNumber; i++) {
- coordinates.push(parseCoordinates(byteOrder));
- }
- if (inner) return coordinates;
- return {
- type: 'LineString',
- coordinates: coordinates
- };
- case 3: //wkbPolygon
- let polygonCoordinates = [];
- const numRings = byteOrder ? geoBuf.readInt32LE(geoPos) : geoBuf.readInt32BE(geoPos);
- geoPos += 4;
- for (let ring = 0; ring < numRings; ring++) {
- const pointNumber = byteOrder ? geoBuf.readInt32LE(geoPos) : geoBuf.readInt32BE(geoPos);
- geoPos += 4;
- let linesCoordinates = [];
- for (let i = 0; i < pointNumber; i++) {
- linesCoordinates.push(parseCoordinates(byteOrder));
- }
- polygonCoordinates.push(linesCoordinates);
- }
- if (inner) return polygonCoordinates;
- return {
- type: 'Polygon',
- coordinates: polygonCoordinates
- };
- case 4: //wkbMultiPoint
- return {
- type: 'MultiPoint',
- coordinates: parseGeomArray(byteOrder, true)
- };
- case 5: //wkbMultiLineString
- return {
- type: 'MultiLineString',
- coordinates: parseGeomArray(byteOrder, true)
- };
- case 6: //wkbMultiPolygon
- return {
- type: 'MultiPolygon',
- coordinates: parseGeomArray(byteOrder, true)
- };
- case 7: //wkbGeometryCollection
- return {
- type: 'GeometryCollection',
- geometries: parseGeomArray(byteOrder, false)
- };
- }
- return null;
- }
- function parseGeomArray(byteOrder, inner) {
- let coordinates = [];
- const number = byteOrder ? geoBuf.readInt32LE(geoPos) : geoBuf.readInt32BE(geoPos);
- geoPos += 4;
- for (let i = 0; i < number; i++) {
- coordinates.push(readGeometryObject(inner));
- }
- return coordinates;
- }
- }
- peek() {
- return this.buf[this.pos];
- }
- remaining() {
- return this.end - this.pos > 0;
- }
- readUInt8() {
- return this.buf[this.pos++];
- }
- readUInt16() {
- return this.buf[this.pos++] + (this.buf[this.pos++] << 8);
- }
- readUInt24() {
- return this.buf[this.pos++] + (this.buf[this.pos++] << 8) + (this.buf[this.pos++] << 16);
- }
- readUInt32() {
- return (
- this.buf[this.pos++] +
- (this.buf[this.pos++] << 8) +
- (this.buf[this.pos++] << 16) +
- this.buf[this.pos++] * 0x1000000
- );
- }
- readInt32() {
- return (
- this.buf[this.pos++] +
- (this.buf[this.pos++] << 8) +
- (this.buf[this.pos++] << 16) +
- (this.buf[this.pos++] << 24)
- );
- }
- readInt32LE() {
- return (
- (this.buf[this.pos++] << 24) +
- (this.buf[this.pos++] << 16) +
- (this.buf[this.pos++] << 8) +
- this.buf[this.pos++]
- );
- }
- readInt64() {
- // could use readBigInt64LE when support would be 10.20+
- const val =
- this.buf[this.pos + 4] +
- this.buf[this.pos + 5] * 2 ** 8 +
- this.buf[this.pos + 6] * 2 ** 16 +
- (this.buf[this.pos + 7] << 24);
- const vv =
- (BigInt(val) << BigInt(32)) +
- BigInt(
- this.buf[this.pos] +
- this.buf[this.pos + 1] * 2 ** 8 +
- this.buf[this.pos + 2] * 2 ** 16 +
- this.buf[this.pos + 3] * 2 ** 24
- );
- this.pos += 8;
- return vv;
- }
- readUnsignedLength() {
- const type = this.buf[this.pos++] & 0xff;
- switch (type) {
- case 0xfb:
- return null;
- case 0xfc:
- return this.readUInt16();
- case 0xfd:
- return this.readUInt24();
- case 0xfe:
- // limitation to BigInt signed value
- return Number(this.readInt64());
- default:
- return type;
- }
- }
- readBuffer(len) {
- this.pos += len;
- return this.buf.slice(this.pos - len, this.pos);
- }
- readBufferRemaining() {
- let b = this.buf.slice(this.pos, this.end);
- this.pos = this.end;
- return b;
- }
- readBufferLengthEncoded() {
- const len = this.readUnsignedLength();
- if (len === null) return null;
- this.pos += len;
- return this.buf.slice(this.pos - len, this.pos);
- }
- readStringNullEnded() {
- let initialPosition = this.pos;
- let cnt = 0;
- while (this.remaining() > 0 && this.buf[this.pos++] !== 0) {
- cnt++;
- }
- return this.buf.toString('utf8', initialPosition, initialPosition + cnt);
- }
- readSignedLength() {
- const type = this.buf[this.pos++];
- switch (type) {
- case 0xfb:
- return null;
- case 0xfc:
- return this.readUInt16();
- case 0xfd:
- return this.readUInt24();
- case 0xfe:
- return Number(this.readInt64());
- default:
- return type;
- }
- }
- readSignedLengthBigInt() {
- const type = this.buf[this.pos++];
- switch (type) {
- case 0xfb:
- return null;
- case 0xfc:
- return BigInt(this.readUInt16());
- case 0xfd:
- return BigInt(this.readUInt24());
- case 0xfe:
- return this.readInt64();
- default:
- return BigInt(type);
- }
- }
- readAsciiStringLengthEncoded() {
- const len = this.readUnsignedLength();
- if (len === null) return null;
- this.pos += len;
- return this.buf.toString('ascii', this.pos - len, this.pos);
- }
- readStringLengthEncoded(encoding) {
- const len = this.readUnsignedLength();
- if (len === null) return null;
- this.pos += len;
- if (Buffer.isEncoding(encoding)) {
- return this.buf.toString(encoding, this.pos - len, this.pos);
- }
- return Iconv.decode(this.buf.slice(this.pos - len, this.pos), encoding);
- }
- readLongLengthEncoded(supportBigInt, supportBigNumbers, bigNumberStrings, unsigned) {
- const len = this.readUnsignedLength();
- if (len === null) return null;
- if (supportBigInt) {
- const str = this.buf.toString('ascii', this.pos, this.pos + len);
- this.pos += len;
- return BigInt(str);
- }
- let result = 0;
- let negate = false;
- let begin = this.pos;
- //minus sign
- if (len > 0 && this.buf[begin] === 45) {
- negate = true;
- begin++;
- }
- for (; begin < this.pos + len; begin++) {
- result = result * 10 + (this.buf[begin] - 48);
- }
- let val = negate ? -1 * result : result;
- this.pos += len;
- if (!Number.isSafeInteger(val)) {
- const str = this.buf.toString('ascii', this.pos - len, this.pos);
- if (bigNumberStrings) return str;
- if (supportBigNumbers) {
- return Long.fromString(str, unsigned, 10);
- }
- }
- return val;
- }
- readDecimalLengthEncoded(bigNumberStrings) {
- const len = this.readUnsignedLength();
- if (len === null) return null;
- this.pos += len;
- let str = this.buf.toString('ascii', this.pos - len, this.pos);
- return bigNumberStrings ? str : +str;
- }
- readDate() {
- const len = this.readUnsignedLength();
- if (len === null) return null;
- let res = [];
- let value = 0;
- let initPos = this.pos;
- this.pos += len;
- while (initPos < this.pos) {
- const char = this.buf[initPos++];
- if (char === 45) {
- //minus separator
- res.push(value);
- value = 0;
- } else {
- value = value * 10 + char - 48;
- }
- }
- res.push(value);
- //handle zero-date as null
- if (res[0] === 0 && res[1] === 0 && res[2] === 0) return null;
- return new Date(res[0], res[1] - 1, res[2]);
- }
- readDateTime(opts) {
- const len = this.readUnsignedLength();
- if (len === null) return null;
- this.pos += len;
- const str = this.buf.toString('ascii', this.pos - len, this.pos);
- if (str.startsWith('0000-00-00 00:00:00')) return null;
- if (opts.tz) {
- return new Date(
- moment.tz(str, opts.tz).clone().tz(opts.localTz).format('YYYY-MM-DD HH:mm:ss.SSSSSS')
- );
- }
- return new Date(str);
- }
- readIntLengthEncoded() {
- const len = this.readUnsignedLength();
- if (len === null) return null;
- let result = 0;
- let negate = false;
- let begin = this.pos;
- if (len > 0 && this.buf[begin] === 45) {
- //minus sign
- negate = true;
- begin++;
- }
- for (; begin < this.pos + len; begin++) {
- result = result * 10 + (this.buf[begin] - 48);
- }
- this.pos += len;
- return negate ? -1 * result : result;
- }
- readFloatLengthCoded() {
- const len = this.readUnsignedLength();
- if (len === null) return null;
- this.pos += len;
- return +this.buf.toString('ascii', this.pos - len, this.pos);
- }
- skipLengthCodedNumber() {
- const type = this.buf[this.pos++] & 0xff;
- switch (type) {
- case 251:
- return;
- case 252:
- this.pos +=
- 2 + (0xffff & ((this.buf[this.pos] & 0xff) + ((this.buf[this.pos + 1] & 0xff) << 8)));
- return;
- case 253:
- this.pos +=
- 3 +
- (0xffffff &
- ((this.buf[this.pos] & 0xff) +
- ((this.buf[this.pos + 1] & 0xff) << 8) +
- ((this.buf[this.pos + 2] & 0xff) << 16)));
- return;
- case 254:
- this.pos +=
- 8 +
- ((this.buf[this.pos] & 0xff) +
- ((this.buf[this.pos + 1] & 0xff) << 8) +
- ((this.buf[this.pos + 2] & 0xff) << 16) +
- ((this.buf[this.pos + 3] & 0xff) << 24) +
- ((this.buf[this.pos + 4] & 0xff) << 32) +
- ((this.buf[this.pos + 5] & 0xff) << 40) +
- ((this.buf[this.pos + 6] & 0xff) << 48) +
- ((this.buf[this.pos + 7] & 0xff) << 56));
- return;
- default:
- this.pos += type;
- return;
- }
- }
- positionFromEnd(num) {
- this.pos = this.end - num;
- }
- /**
- * For testing purpose only
- */
- _toBuf() {
- return this.buf.slice(this.pos, this.end);
- }
- forceOffset(off) {
- this.pos = off;
- }
- length() {
- return this.end - this.pos;
- }
- subPacketLengthEncoded() {
- const len = this.readUnsignedLength();
- this.skip(len);
- return new Packet(this.buf, this.pos - len, this.pos);
- }
- /**
- * Parse ERR_Packet : https://mariadb.com/kb/en/library/err_packet/
- *
- * @param info current connection info
- * @param sql command sql
- * @param stack additional stack trace
- * @returns {Error}
- */
- readError(info, sql, stack) {
- this.skip(1);
- let errorCode = this.readUInt16();
- let sqlState = '';
- if (this.peek() === 0x23) {
- this.skip(6);
- sqlState = this.buf.toString('utf8', this.pos - 5, this.pos);
- }
- let msg = this.buf.toString('utf8', this.pos, this.end);
- if (sql) msg += '\n' + sql;
- let fatal = sqlState.startsWith('08') || sqlState === '70100';
- if (fatal) {
- const packetMsgs = info.getLastPackets();
- if (packetMsgs !== '')
- return Errors.createError(
- msg + '\nlast received packets:\n' + packetMsgs,
- fatal,
- info,
- sqlState,
- errorCode,
- stack
- );
- }
- return Errors.createError(msg, fatal, info, sqlState, errorCode, stack);
- }
- }
- module.exports = Packet;
|