UNPKG

tedious

Version:

A TDS driver, for connecting to MS SQLServer databases.

344 lines (294 loc) 9.37 kB
"use strict"; const Transform = require('readable-stream').Transform; const TYPE = require('./token').TYPE; const tokenParsers = {}; tokenParsers[TYPE.COLMETADATA] = require('./colmetadata-token-parser'); tokenParsers[TYPE.DONE] = require('./done-token-parser').doneParser; tokenParsers[TYPE.DONEINPROC] = require('./done-token-parser').doneInProcParser; tokenParsers[TYPE.DONEPROC] = require('./done-token-parser').doneProcParser; tokenParsers[TYPE.ENVCHANGE] = require('./env-change-token-parser'); tokenParsers[TYPE.ERROR] = require('./infoerror-token-parser').errorParser; tokenParsers[TYPE.FEDAUTHINFO] = require('./fedauth-info-parser'); tokenParsers[TYPE.FEATUREEXTACK] = require('./feature-ext-ack-parser'); tokenParsers[TYPE.INFO] = require('./infoerror-token-parser').infoParser; tokenParsers[TYPE.LOGINACK] = require('./loginack-token-parser'); tokenParsers[TYPE.ORDER] = require('./order-token-parser'); tokenParsers[TYPE.RETURNSTATUS] = require('./returnstatus-token-parser'); tokenParsers[TYPE.RETURNVALUE] = require('./returnvalue-token-parser'); tokenParsers[TYPE.ROW] = require('./row-token-parser'); tokenParsers[TYPE.NBCROW] = require('./nbcrow-token-parser'); tokenParsers[TYPE.SSPI] = require('./sspi-token-parser'); module.exports = class Parser extends Transform { constructor(debug, colMetadata, options) { super({ objectMode: true }); this.debug = debug; this.colMetadata = colMetadata; this.options = options; this.endOfMessageMarker = {}; this.buffer = Buffer.alloc(0); this.position = 0; this.suspended = false; this.next = undefined; } _transform(input, encoding, done) { if (input === this.endOfMessageMarker) { done(null, { // generate endOfMessage pseudo token name: 'EOM', event: 'endOfMessage' }); return; } if (this.position === this.buffer.length) { this.buffer = input; } else { this.buffer = Buffer.concat([this.buffer.slice(this.position), input]); } this.position = 0; if (this.suspended) { // Unsuspend and continue from where ever we left off. this.suspended = false; this.next.call(null); } // If we're no longer suspended, parse new tokens if (!this.suspended) { // Start the parser this.parseTokens(); } done(); } parseTokens() { const doneParsing = token => { if (token) { switch (token.name) { case 'COLMETADATA': this.colMetadata = token.columns; } this.push(token); } }; while (!this.suspended && this.position + 1 <= this.buffer.length) { const type = this.buffer.readUInt8(this.position); this.position += 1; if (tokenParsers[type]) { tokenParsers[type](this, this.colMetadata, this.options, doneParsing); } else { this.emit('error', new Error('Unknown type: ' + type)); } } } suspend(next) { this.suspended = true; this.next = next; } awaitData(length, callback) { if (this.position + length <= this.buffer.length) { callback(); } else { this.suspend(() => { this.awaitData(length, callback); }); } } readInt8(callback) { this.awaitData(1, () => { const data = this.buffer.readInt8(this.position); this.position += 1; callback(data); }); } readUInt8(callback) { this.awaitData(1, () => { const data = this.buffer.readUInt8(this.position); this.position += 1; callback(data); }); } readInt16LE(callback) { this.awaitData(2, () => { const data = this.buffer.readInt16LE(this.position); this.position += 2; callback(data); }); } readInt16BE(callback) { this.awaitData(2, () => { const data = this.buffer.readInt16BE(this.position); this.position += 2; callback(data); }); } readUInt16LE(callback) { this.awaitData(2, () => { const data = this.buffer.readUInt16LE(this.position); this.position += 2; callback(data); }); } readUInt16BE(callback) { this.awaitData(2, () => { const data = this.buffer.readUInt16BE(this.position); this.position += 2; callback(data); }); } readInt32LE(callback) { this.awaitData(4, () => { const data = this.buffer.readInt32LE(this.position); this.position += 4; callback(data); }); } readInt32BE(callback) { this.awaitData(4, () => { const data = this.buffer.readInt32BE(this.position); this.position += 4; callback(data); }); } readUInt32LE(callback) { this.awaitData(4, () => { const data = this.buffer.readUInt32LE(this.position); this.position += 4; callback(data); }); } readUInt32BE(callback) { this.awaitData(4, () => { const data = this.buffer.readUInt32BE(this.position); this.position += 4; callback(data); }); } readInt64LE(callback) { this.awaitData(8, () => { const data = Math.pow(2, 32) * this.buffer.readInt32LE(this.position + 4) + ((this.buffer[this.position + 4] & 0x80) === 0x80 ? 1 : -1) * this.buffer.readUInt32LE(this.position); this.position += 8; callback(data); }); } readInt64BE(callback) { this.awaitData(8, () => { const data = Math.pow(2, 32) * this.buffer.readInt32BE(this.position) + ((this.buffer[this.position] & 0x80) === 0x80 ? 1 : -1) * this.buffer.readUInt32BE(this.position + 4); this.position += 8; callback(data); }); } readUInt64LE(callback) { this.awaitData(8, () => { const data = Math.pow(2, 32) * this.buffer.readUInt32LE(this.position + 4) + this.buffer.readUInt32LE(this.position); this.position += 8; callback(data); }); } readUInt64BE(callback) { this.awaitData(8, () => { const data = Math.pow(2, 32) * this.buffer.readUInt32BE(this.position) + this.buffer.readUInt32BE(this.position + 4); this.position += 8; callback(data); }); } readFloatLE(callback) { this.awaitData(4, () => { const data = this.buffer.readFloatLE(this.position); this.position += 4; callback(data); }); } readFloatBE(callback) { this.awaitData(4, () => { const data = this.buffer.readFloatBE(this.position); this.position += 4; callback(data); }); } readDoubleLE(callback) { this.awaitData(8, () => { const data = this.buffer.readDoubleLE(this.position); this.position += 8; callback(data); }); } readDoubleBE(callback) { this.awaitData(8, () => { const data = this.buffer.readDoubleBE(this.position); this.position += 8; callback(data); }); } readUInt24LE(callback) { this.awaitData(3, () => { const low = this.buffer.readUInt16LE(this.position); const high = this.buffer.readUInt8(this.position + 2); this.position += 3; callback(low | high << 16); }); } readUInt40LE(callback) { this.awaitData(5, () => { const low = this.buffer.readUInt32LE(this.position); const high = this.buffer.readUInt8(this.position + 4); this.position += 5; callback(0x100000000 * high + low); }); } readUNumeric64LE(callback) { this.awaitData(8, () => { const low = this.buffer.readUInt32LE(this.position); const high = this.buffer.readUInt32LE(this.position + 4); this.position += 8; callback(0x100000000 * high + low); }); } readUNumeric96LE(callback) { this.awaitData(12, () => { const dword1 = this.buffer.readUInt32LE(this.position); const dword2 = this.buffer.readUInt32LE(this.position + 4); const dword3 = this.buffer.readUInt32LE(this.position + 8); this.position += 12; callback(dword1 + 0x100000000 * dword2 + 0x100000000 * 0x100000000 * dword3); }); } readUNumeric128LE(callback) { this.awaitData(16, () => { const dword1 = this.buffer.readUInt32LE(this.position); const dword2 = this.buffer.readUInt32LE(this.position + 4); const dword3 = this.buffer.readUInt32LE(this.position + 8); const dword4 = this.buffer.readUInt32LE(this.position + 12); this.position += 16; callback(dword1 + 0x100000000 * dword2 + 0x100000000 * 0x100000000 * dword3 + 0x100000000 * 0x100000000 * 0x100000000 * dword4); }); } // Variable length data readBuffer(length, callback) { this.awaitData(length, () => { const data = this.buffer.slice(this.position, this.position + length); this.position += length; callback(data); }); } // Read a Unicode String (BVARCHAR) readBVarChar(callback) { this.readUInt8(length => { this.readBuffer(length * 2, data => { callback(data.toString('ucs2')); }); }); } // Read a Unicode String (USVARCHAR) readUsVarChar(callback) { this.readUInt16LE(length => { this.readBuffer(length * 2, data => { callback(data.toString('ucs2')); }); }); } // Read binary data (BVARBYTE) readBVarByte(callback) { this.readUInt8(length => { this.readBuffer(length, callback); }); } // Read binary data (USVARBYTE) readUsVarByte(callback) { this.readUInt16LE(length => { this.readBuffer(length, callback); }); } };