UNPKG

tedious

Version:

A TDS driver, for connecting to MS SQLServer databases.

640 lines (522 loc) 17.5 kB
"use strict"; const iconv = require('iconv-lite'); const sprintf = require('sprintf-js').sprintf; const TYPE = require('./data-type').TYPE; const guidParser = require('./guid-parser'); const readPrecision = require('./metadata-parser').readPrecision; const readScale = require('./metadata-parser').readScale; const readCollation = require('./metadata-parser').readCollation; const convertLEBytesToString = require('./tracking-buffer/bigint').convertLEBytesToString; const NULL = (1 << 16) - 1; const MAX = (1 << 16) - 1; const THREE_AND_A_THIRD = 3 + 1 / 3; const MONEY_DIVISOR = 10000; const PLP_NULL = Buffer.from([0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]); const UNKNOWN_PLP_LEN = Buffer.from([0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]); const DEFAULT_ENCODING = 'utf8'; function readTextPointerNull(parser, type, callback) { if (type.hasTextPointerAndTimestamp) { parser.readUInt8(textPointerLength => { if (textPointerLength !== 0) { // Appear to be dummy values, so consume and discard them. parser.readBuffer(textPointerLength, () => { parser.readBuffer(8, () => { callback(undefined); }); }); } else { callback(true); } }); } else { callback(undefined); } } function readDataLength(parser, type, metaData, textPointerNull, callback) { if (textPointerNull) { return callback(0); } if (metaData.isVariantValue) { return callback(metaData.dataLength); } // s2.2.4.2.1 switch (type.id & 0x30) { case 0x10: // xx01xxxx - s2.2.4.2.1.1 return callback(0); case 0x20: // xx10xxxx - s2.2.4.2.1.3 // Variable length if (metaData.dataLength !== MAX) { switch (type.dataLengthLength) { case 0: return callback(undefined); case 1: return parser.readUInt8(callback); case 2: return parser.readUInt16LE(callback); case 4: return parser.readUInt32LE(callback); default: return parser.emit('error', new Error('Unsupported dataLengthLength ' + type.dataLengthLength + ' for data type ' + type.name)); } } else { return callback(undefined); } case 0x30: return callback(1 << ((type.id & 0x0C) >> 2)); } } module.exports = valueParse; function valueParse(parser, metaData, options, callback) { const type = metaData.type; readTextPointerNull(parser, type, textPointerNull => { readDataLength(parser, type, metaData, textPointerNull, dataLength => { switch (type.name) { case 'Null': return callback(null); case 'TinyInt': return parser.readUInt8(callback); case 'Int': return parser.readInt32LE(callback); case 'SmallInt': return parser.readInt16LE(callback); case 'BigInt': return parser.readBuffer(8, buffer => { callback(convertLEBytesToString(buffer)); }); case 'IntN': switch (dataLength) { case 0: return callback(null); case 1: return parser.readUInt8(callback); case 2: return parser.readInt16LE(callback); case 4: return parser.readInt32LE(callback); case 8: return parser.readBuffer(8, buffer => { callback(convertLEBytesToString(buffer)); }); default: return parser.emit('error', new Error('Unsupported dataLength ' + dataLength + ' for IntN')); } case 'Real': return parser.readFloatLE(callback); case 'Float': return parser.readDoubleLE(callback); case 'FloatN': switch (dataLength) { case 0: return callback(null); case 4: return parser.readFloatLE(callback); case 8: return parser.readDoubleLE(callback); default: return parser.emit('error', new Error('Unsupported dataLength ' + dataLength + ' for FloatN')); } case 'Money': case 'SmallMoney': case 'MoneyN': switch (dataLength) { case 0: return callback(null); case 4: return parser.readInt32LE(value => { callback(value / MONEY_DIVISOR); }); case 8: return parser.readInt32LE(high => { parser.readUInt32LE(low => { callback((low + 0x100000000 * high) / MONEY_DIVISOR); }); }); default: return parser.emit('error', new Error('Unsupported dataLength ' + dataLength + ' for MoneyN')); } case 'Bit': return parser.readUInt8(value => { callback(!!value); }); case 'BitN': switch (dataLength) { case 0: return callback(null); case 1: return parser.readUInt8(value => { callback(!!value); }); default: return parser.emit('error', new Error('Unsupported dataLength ' + dataLength + ' for BitN')); } case 'VarChar': case 'Char': const codepage = metaData.collation.codepage; if (metaData.dataLength === MAX) { return readMaxChars(parser, codepage, callback); } else { return readChars(parser, dataLength, codepage, NULL, callback); } case 'NVarChar': case 'NChar': if (metaData.dataLength === MAX) { return readMaxNChars(parser, callback); } else { return readNChars(parser, dataLength, NULL, callback); } case 'VarBinary': case 'Binary': if (metaData.dataLength === MAX) { return readMaxBinary(parser, callback); } else { return readBinary(parser, dataLength, NULL, callback); } case 'Text': if (textPointerNull) { return callback(null); } else { return readChars(parser, dataLength, metaData.collation.codepage, PLP_NULL, callback); } case 'NText': if (textPointerNull) { return callback(null); } else { return readNChars(parser, dataLength, PLP_NULL, callback); } case 'Image': if (textPointerNull) { return callback(null); } else { return readBinary(parser, dataLength, PLP_NULL, callback); } case 'Xml': return readMaxNChars(parser, callback); case 'SmallDateTime': return readSmallDateTime(parser, options.useUTC, callback); case 'DateTime': return readDateTime(parser, options.useUTC, callback); case 'DateTimeN': switch (dataLength) { case 0: return callback(null); case 4: return readSmallDateTime(parser, options.useUTC, callback); case 8: return readDateTime(parser, options.useUTC, callback); default: return parser.emit('error', new Error('Unsupported dataLength ' + dataLength + ' for DateTimeN')); } case 'Time': if (dataLength === 0) { return callback(null); } else { return readTime(parser, dataLength, metaData.scale, options.useUTC, callback); } case 'Date': if (dataLength === 0) { return callback(null); } else { return readDate(parser, options.useUTC, callback); } case 'DateTime2': if (dataLength === 0) { return callback(null); } else { return readDateTime2(parser, dataLength, metaData.scale, options.useUTC, callback); } case 'DateTimeOffset': if (dataLength === 0) { return callback(null); } else { return readDateTimeOffset(parser, dataLength, metaData.scale, callback); } case 'NumericN': case 'DecimalN': if (dataLength === 0) { return callback(null); } else { return parser.readUInt8(sign => { sign = sign === 1 ? 1 : -1; let readValue; switch (dataLength - 1) { case 4: readValue = parser.readUInt32LE; break; case 8: readValue = parser.readUNumeric64LE; break; case 12: readValue = parser.readUNumeric96LE; break; case 16: readValue = parser.readUNumeric128LE; break; default: return parser.emit('error', new Error(sprintf('Unsupported numeric size %d', dataLength - 1))); } readValue.call(parser, value => { callback(value * sign / Math.pow(10, metaData.scale)); }); }); } case 'UniqueIdentifier': switch (dataLength) { case 0: return callback(null); case 0x10: return parser.readBuffer(0x10, data => { callback(guidParser.arrayToGuid(data)); }); default: return parser.emit('error', new Error(sprintf('Unsupported guid size %d', dataLength - 1))); } case 'UDT': return readMaxBinary(parser, callback); case 'Variant': if (dataLength === 0) { return callback(null); } const valueMetaData = metaData.valueMetaData = {}; Object.defineProperty(valueMetaData, 'isVariantValue', { value: true }); return parser.readUInt8(baseType => { return parser.readUInt8(propBytes => { valueMetaData.dataLength = dataLength - propBytes - 2; valueMetaData.type = TYPE[baseType]; return readPrecision(parser, valueMetaData.type, precision => { valueMetaData.precision = precision; return readScale(parser, valueMetaData.type, scale => { valueMetaData.scale = scale; return readCollation(parser, valueMetaData.type, collation => { valueMetaData.collation = collation; if (baseType === 0xA5 || baseType === 0xAD || baseType === 0xA7 || baseType === 0xAF || baseType === 0xE7 || baseType === 0xEF) { return readDataLength(parser, valueMetaData.type, {}, null, maxDataLength => { // skip the 2-byte max length sent for BIGVARCHRTYPE, BIGCHARTYPE, NVARCHARTYPE, NCHARTYPE, BIGVARBINTYPE and BIGBINARYTYPE types // and parse based on the length of actual data return valueParse(parser, valueMetaData, options, callback); }); } else { return valueParse(parser, valueMetaData, options, callback); } }); }); }); }); }); default: return parser.emit('error', new Error(sprintf('Unrecognised type %s', type.name))); } }); }); } function readBinary(parser, dataLength, nullValue, callback) { if (dataLength === nullValue) { return callback(null); } else { return parser.readBuffer(dataLength, callback); } } function readChars(parser, dataLength, codepage, nullValue, callback) { if (codepage == null) { codepage = DEFAULT_ENCODING; } if (dataLength === nullValue) { return callback(null); } else { return parser.readBuffer(dataLength, data => { callback(iconv.decode(data, codepage)); }); } } function readNChars(parser, dataLength, nullValue, callback) { if (dataLength === nullValue) { return callback(null); } else { return parser.readBuffer(dataLength, data => { callback(data.toString('ucs2')); }); } } function readMaxBinary(parser, callback) { return readMax(parser, callback); } function readMaxChars(parser, codepage, callback) { if (codepage == null) { codepage = DEFAULT_ENCODING; } readMax(parser, data => { if (data) { callback(iconv.decode(data, codepage)); } else { callback(null); } }); } function readMaxNChars(parser, callback) { readMax(parser, data => { if (data) { callback(data.toString('ucs2')); } else { callback(null); } }); } function readMax(parser, callback) { parser.readBuffer(8, type => { if (type.equals(PLP_NULL)) { return callback(null); } else if (type.equals(UNKNOWN_PLP_LEN)) { return readMaxUnknownLength(parser, callback); } else { const low = type.readUInt32LE(0); const high = type.readUInt32LE(4); if (high >= 2 << 53 - 32) { console.warn('Read UInt64LE > 53 bits : high=' + high + ', low=' + low); } const expectedLength = low + 0x100000000 * high; return readMaxKnownLength(parser, expectedLength, callback); } }); } function readMaxKnownLength(parser, totalLength, callback) { const data = Buffer.alloc(totalLength, 0); let offset = 0; function next(done) { parser.readUInt32LE(chunkLength => { if (!chunkLength) { return done(); } parser.readBuffer(chunkLength, chunk => { chunk.copy(data, offset); offset += chunkLength; next(done); }); }); } next(() => { if (offset !== totalLength) { parser.emit('error', new Error('Partially Length-prefixed Bytes unmatched lengths : expected ' + totalLength + ', but got ' + offset + ' bytes')); } callback(data); }); } function readMaxUnknownLength(parser, callback) { const chunks = []; let length = 0; function next(done) { parser.readUInt32LE(chunkLength => { if (!chunkLength) { return done(); } parser.readBuffer(chunkLength, chunk => { chunks.push(chunk); length += chunkLength; next(done); }); }); } next(() => { callback(Buffer.concat(chunks, length)); }); } function readSmallDateTime(parser, useUTC, callback) { parser.readUInt16LE(days => { parser.readUInt16LE(minutes => { let value; if (useUTC) { value = new Date(Date.UTC(1900, 0, 1 + days, 0, minutes)); } else { value = new Date(1900, 0, 1 + days, 0, minutes); } callback(value); }); }); } function readDateTime(parser, useUTC, callback) { parser.readInt32LE(days => { parser.readUInt32LE(threeHundredthsOfSecond => { const milliseconds = Math.round(threeHundredthsOfSecond * THREE_AND_A_THIRD); let value; if (useUTC) { value = new Date(Date.UTC(1900, 0, 1 + days, 0, 0, 0, milliseconds)); } else { value = new Date(1900, 0, 1 + days, 0, 0, 0, milliseconds); } callback(value); }); }); } function readTime(parser, dataLength, scale, useUTC, callback) { let readValue; switch (dataLength) { case 3: readValue = parser.readUInt24LE; break; case 4: readValue = parser.readUInt32LE; break; case 5: readValue = parser.readUInt40LE; } readValue.call(parser, value => { if (scale < 7) { for (let i = scale; i < 7; i++) { value *= 10; } } let date; if (useUTC) { date = new Date(Date.UTC(1970, 0, 1, 0, 0, 0, value / 10000)); } else { date = new Date(1970, 0, 1, 0, 0, 0, value / 10000); } Object.defineProperty(date, 'nanosecondsDelta', { enumerable: false, value: value % 10000 / Math.pow(10, 7) }); callback(date); }); } function readDate(parser, useUTC, callback) { parser.readUInt24LE(days => { if (useUTC) { callback(new Date(Date.UTC(2000, 0, days - 730118))); } else { callback(new Date(2000, 0, days - 730118)); } }); } function readDateTime2(parser, dataLength, scale, useUTC, callback) { readTime(parser, dataLength - 3, scale, useUTC, time => { parser.readUInt24LE(days => { let date; if (useUTC) { date = new Date(Date.UTC(2000, 0, days - 730118, 0, 0, 0, +time)); } else { date = new Date(2000, 0, days - 730118, time.getHours(), time.getMinutes(), time.getSeconds(), time.getMilliseconds()); } Object.defineProperty(date, 'nanosecondsDelta', { enumerable: false, value: time.nanosecondsDelta }); callback(date); }); }); } function readDateTimeOffset(parser, dataLength, scale, callback) { readTime(parser, dataLength - 5, scale, true, time => { parser.readUInt24LE(days => { // offset parser.readInt16LE(() => { const date = new Date(Date.UTC(2000, 0, days - 730118, 0, 0, 0, +time)); Object.defineProperty(date, 'nanosecondsDelta', { enumerable: false, value: time.nanosecondsDelta }); callback(date); }); }); }); }