tedious
Version:
A TDS driver, for connecting to MS SQLServer databases.
479 lines (452 loc) • 12.9 kB
JavaScript
// Generated by CoffeeScript 1.7.1
var DEFAULT_ENCODING, MAX, MONEY_DIVISOR, NULL, PLP_NULL, THREE_AND_A_THIRD, UNKNOWN_PLP_LEN, guidParser, iconv, parse, readBinary, readChars, readDate, readDateTime, readDateTime2, readDateTimeOffset, readMax, readMaxBinary, readMaxChars, readMaxNChars, readNChars, readSmallDateTime, readTime, sprintf;
iconv = require('iconv-lite');
sprintf = require('sprintf').sprintf;
guidParser = require('./guid-parser');
require('./buffertools');
NULL = (1 << 16) - 1;
MAX = (1 << 16) - 1;
THREE_AND_A_THIRD = 3 + (1 / 3);
MONEY_DIVISOR = 10000;
PLP_NULL = new Buffer([0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]);
UNKNOWN_PLP_LEN = new Buffer([0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]);
DEFAULT_ENCODING = 'utf8';
parse = function(buffer, metaData, options) {
var codepage, dataLength, high, low, sign, textPointerLength, textPointerNull, type, value;
value = void 0;
dataLength = void 0;
textPointerNull = void 0;
type = metaData.type;
if (type.hasTextPointerAndTimestamp) {
textPointerLength = buffer.readUInt8();
if (textPointerLength !== 0) {
buffer.readBuffer(textPointerLength);
buffer.readBuffer(8);
} else {
dataLength = 0;
textPointerNull = true;
}
}
if (!dataLength && dataLength !== 0) {
switch (type.id & 0x30) {
case 0x10:
dataLength = 0;
break;
case 0x20:
if (metaData.dataLength !== MAX) {
switch (type.dataLengthLength) {
case 0:
dataLength = void 0;
break;
case 1:
dataLength = buffer.readUInt8();
break;
case 2:
dataLength = buffer.readUInt16LE();
break;
case 4:
dataLength = buffer.readUInt32LE();
break;
default:
throw Error("Unsupported dataLengthLength " + type.dataLengthLength + " for data type " + type.name);
}
}
break;
case 0x30:
dataLength = 1 << ((type.id & 0x0C) >> 2);
}
}
switch (type.name) {
case 'Null':
value = null;
break;
case 'TinyInt':
value = buffer.readUInt8();
break;
case 'Int':
value = buffer.readInt32LE();
break;
case 'SmallInt':
value = buffer.readInt16LE();
break;
case 'BigInt':
value = buffer.readAsStringInt64LE();
break;
case 'IntN':
switch (dataLength) {
case 0:
value = null;
break;
case 1:
value = buffer.readUInt8();
break;
case 2:
value = buffer.readInt16LE();
break;
case 4:
value = buffer.readInt32LE();
break;
case 8:
value = buffer.readAsStringInt64LE();
break;
default:
throw new Error("Unsupported dataLength " + dataLength + " for IntN");
}
break;
case 'Real':
value = buffer.readFloatLE();
break;
case 'Float':
value = buffer.readDoubleLE();
break;
case 'FloatN':
switch (dataLength) {
case 0:
value = null;
break;
case 4:
value = buffer.readFloatLE();
break;
case 8:
value = buffer.readDoubleLE();
break;
default:
throw new Error("Unsupported dataLength " + dataLength + " for FloatN");
}
break;
case 'Money':
case 'SmallMoney':
case 'MoneyN':
switch (dataLength) {
case 0:
value = null;
break;
case 4:
value = buffer.readInt32LE() / MONEY_DIVISOR;
break;
case 8:
high = buffer.readInt32LE();
low = buffer.readUInt32LE();
value = low + (0x100000000 * high);
value /= MONEY_DIVISOR;
break;
default:
throw new Error("Unsupported dataLength " + dataLength + " for MoneyN");
}
break;
case 'Bit':
value = !!buffer.readUInt8();
break;
case 'BitN':
switch (dataLength) {
case 0:
value = null;
break;
case 1:
value = !!buffer.readUInt8();
}
break;
case 'VarChar':
case 'Char':
codepage = metaData.collation.codepage;
if (metaData.dataLength === MAX) {
value = readMaxChars(buffer, codepage);
} else {
value = readChars(buffer, dataLength, codepage);
}
break;
case 'NVarChar':
case 'NChar':
if (metaData.dataLength === MAX) {
value = readMaxNChars(buffer);
} else {
value = readNChars(buffer, dataLength);
}
break;
case 'VarBinary':
case 'Binary':
if (metaData.dataLength === MAX) {
value = readMaxBinary(buffer);
} else {
value = readBinary(buffer, dataLength);
}
break;
case 'Text':
if (textPointerNull) {
value = null;
} else {
value = readChars(buffer, dataLength, metaData.collation.codepage);
}
break;
case 'NText':
if (textPointerNull) {
value = null;
} else {
value = readNChars(buffer, dataLength);
}
break;
case 'Image':
if (textPointerNull) {
value = null;
} else {
value = readBinary(buffer, dataLength);
}
break;
case 'Xml':
value = readMaxNChars(buffer);
break;
case 'SmallDateTime':
value = readSmallDateTime(buffer, options.useUTC);
break;
case 'DateTime':
value = readDateTime(buffer, options.useUTC);
break;
case 'DateTimeN':
switch (dataLength) {
case 0:
value = null;
break;
case 4:
value = readSmallDateTime(buffer, options.useUTC);
break;
case 8:
value = readDateTime(buffer, options.useUTC);
}
break;
case 'TimeN':
if ((dataLength = buffer.readUInt8()) === 0) {
value = null;
} else {
value = readTime(buffer, dataLength, metaData.scale);
}
break;
case 'DateN':
if ((dataLength = buffer.readUInt8()) === 0) {
value = null;
} else {
value = readDate(buffer);
}
break;
case 'DateTime2N':
if ((dataLength = buffer.readUInt8()) === 0) {
value = null;
} else {
value = readDateTime2(buffer, dataLength, metaData.scale);
}
break;
case 'DateTimeOffsetN':
if ((dataLength = buffer.readUInt8()) === 0) {
value = null;
} else {
value = readDateTimeOffset(buffer, dataLength, metaData.scale);
}
break;
case 'NumericN':
case 'DecimalN':
if (dataLength === 0) {
value = null;
} else {
sign = buffer.readUInt8() === 1 ? 1 : -1;
switch (dataLength - 1) {
case 4:
value = buffer.readUInt32LE();
break;
case 8:
value = buffer.readUNumeric64LE();
break;
case 12:
value = buffer.readUNumeric96LE();
break;
case 16:
value = buffer.readUNumeric128LE();
break;
default:
throw new Error(sprintf('Unsupported numeric size %d at offset 0x%04X', dataLength - 1, buffer.position));
break;
}
value *= sign;
value /= Math.pow(10, metaData.scale);
}
break;
case 'UniqueIdentifierN':
switch (dataLength) {
case 0:
value = null;
break;
case 0x10:
value = guidParser.arrayToGuid(buffer.readArray(0x10));
break;
default:
throw new Error(sprintf('Unsupported guid size %d at offset 0x%04X', dataLength - 1, buffer.position));
}
break;
case 'UDT':
value = readMaxBinary(buffer);
break;
default:
throw new Error(sprintf('Unrecognised type %s at offset 0x%04X', type.name, buffer.position));
break;
}
return value;
};
readBinary = function(buffer, dataLength) {
if (dataLength === NULL) {
return null;
} else {
return buffer.readBuffer(dataLength);
}
};
readChars = function(buffer, dataLength, codepage) {
if (codepage == null) {
codepage = DEFAULT_ENCODING;
}
if (dataLength === NULL) {
return null;
} else {
return iconv.decode(buffer.readBuffer(dataLength), codepage);
}
};
readNChars = function(buffer, dataLength) {
if (dataLength === NULL) {
return null;
} else {
return buffer.readString(dataLength, 'ucs2');
}
};
readMaxBinary = function(buffer) {
return readMax(buffer, function(valueBuffer) {
return valueBuffer;
});
};
readMaxChars = function(buffer, codepage) {
if (codepage == null) {
codepage = DEFAULT_ENCODING;
}
return readMax(buffer, function(valueBuffer) {
return iconv.decode(valueBuffer, codepage);
});
};
readMaxNChars = function(buffer) {
return readMax(buffer, function(valueBuffer) {
return valueBuffer.toString('ucs2');
});
};
readMax = function(buffer, decodeFunction) {
var chunk, chunkLength, chunks, expectedLength, length, position, type, valueBuffer, _i, _len;
type = buffer.readBuffer(8);
if (type.equals(PLP_NULL)) {
return null;
} else {
if (type.equals(UNKNOWN_PLP_LEN)) {
expectedLength = void 0;
} else {
buffer.rollback();
expectedLength = buffer.readUInt64LE();
}
length = 0;
chunks = [];
chunkLength = buffer.readUInt32LE();
while (chunkLength !== 0) {
length += chunkLength;
chunks.push(buffer.readBuffer(chunkLength));
chunkLength = buffer.readUInt32LE();
}
if (expectedLength) {
if (length !== expectedLength) {
throw new Error("Partially Length-prefixed Bytes unmatched lengths : expected " + expectedLength + ", but got " + length + " bytes");
}
}
valueBuffer = new Buffer(length);
position = 0;
for (_i = 0, _len = chunks.length; _i < _len; _i++) {
chunk = chunks[_i];
chunk.copy(valueBuffer, position, 0);
position += chunk.length;
}
return decodeFunction(valueBuffer);
}
};
readSmallDateTime = function(buffer, useUTC) {
var days, minutes, value;
days = buffer.readUInt16LE();
minutes = buffer.readUInt16LE();
if (useUTC) {
value = new Date(Date.UTC(1900, 0, 1));
value.setUTCDate(value.getUTCDate() + days);
value.setUTCMinutes(value.getUTCMinutes() + minutes);
} else {
value = new Date(1900, 0, 1);
value.setDate(value.getDate() + days);
value.setMinutes(value.getMinutes() + minutes);
}
return value;
};
readDateTime = function(buffer, useUTC) {
var days, milliseconds, threeHundredthsOfSecond, value;
days = buffer.readInt32LE();
threeHundredthsOfSecond = buffer.readUInt32LE();
milliseconds = threeHundredthsOfSecond * THREE_AND_A_THIRD;
if (useUTC) {
value = new Date(Date.UTC(1900, 0, 1));
value.setUTCDate(value.getUTCDate() + days);
value.setUTCMilliseconds(value.getUTCMilliseconds() + milliseconds);
} else {
value = new Date(1900, 0, 1);
value.setDate(value.getDate() + days);
value.setMilliseconds(value.getMilliseconds() + milliseconds);
}
return value;
};
readTime = function(buffer, dataLength, scale) {
var date, i, value, _i, _ref;
switch (dataLength) {
case 3:
value = buffer.readUInt24LE();
break;
case 4:
value = buffer.readUInt32LE();
break;
case 5:
value = buffer.readUInt40LE();
}
if (scale < 7) {
for (i = _i = _ref = scale + 1; _ref <= 7 ? _i <= 7 : _i >= 7; i = _ref <= 7 ? ++_i : --_i) {
value *= 10;
}
}
date = new Date(Date.UTC(1970, 0, 1, 0, 0, 0, value / 10000));
Object.defineProperty(date, "nanosecondsDelta", {
enumerable: false,
value: (value % 10000) / Math.pow(10, 7)
});
return date;
};
readDate = function(buffer) {
var days;
days = buffer.readUInt24LE();
return new Date(Date.UTC(2000, 0, days - 730118));
};
readDateTime2 = function(buffer, dataLength, scale) {
var date, days, time;
time = readTime(buffer, dataLength - 3, scale);
days = buffer.readUInt24LE();
date = new Date(Date.UTC(2000, 0, days - 730118, 0, 0, 0, +time));
Object.defineProperty(date, "nanosecondsDelta", {
enumerable: false,
value: time.nanosecondsDelta
});
return date;
};
readDateTimeOffset = function(buffer, dataLength, scale) {
var date, days, offset, time;
time = readTime(buffer, dataLength - 5, scale);
days = buffer.readUInt24LE();
offset = buffer.readInt16LE();
date = new Date(Date.UTC(2000, 0, days - 730118, 0, 0, 0, +time));
Object.defineProperty(date, "nanosecondsDelta", {
enumerable: false,
value: time.nanosecondsDelta
});
return date;
};
module.exports = parse;