node-firebird-driver
Version:
Firebird Driver Interfaces for Node.js
460 lines (452 loc) • 20.5 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.charSets = exports.cancelType = exports.commonInfo = exports.statementInfo = exports.blobInfo = exports.epb = exports.bpb = exports.tpb = exports.dpb = exports.sqlTypes = void 0;
exports.createDpb = createDpb;
exports.createTpb = createTpb;
exports.createBpb = createBpb;
exports.getPortableInteger = getPortableInteger;
exports.createDataReader = createDataReader;
exports.createDataWriter = createDataWriter;
const os = require("os");
const littleEndian = os.endianness() === 'LE';
const stringDecoder = require("string_decoder");
const date_time_1 = require("./date-time");
const time_zones_1 = require("./time-zones");
const __1 = require("..");
/** SQL_* type constants */
var sqlTypes;
(function (sqlTypes) {
sqlTypes.SQL_TEXT = 452;
sqlTypes.SQL_VARYING = 448;
sqlTypes.SQL_SHORT = 500;
sqlTypes.SQL_LONG = 496;
sqlTypes.SQL_FLOAT = 482;
sqlTypes.SQL_DOUBLE = 480;
//export const SQL_D_FLOAT = 530;
sqlTypes.SQL_TIMESTAMP = 510;
sqlTypes.SQL_BLOB = 520;
//export const SQL_ARRAY = 540;
//export const SQL_QUAD = 550;
sqlTypes.SQL_TYPE_TIME = 560;
sqlTypes.SQL_TYPE_DATE = 570;
sqlTypes.SQL_INT64 = 580;
sqlTypes.SQL_TIMESTAMP_TZ_EX = 32748;
sqlTypes.SQL_TIME_TZ_EX = 32750;
sqlTypes.SQL_TIMESTAMP_TZ = 32754;
sqlTypes.SQL_TIME_TZ = 32756;
sqlTypes.SQL_INT128 = 32752;
sqlTypes.SQL_DEC16 = 32760;
sqlTypes.SQL_DEC34 = 32762;
sqlTypes.SQL_BOOLEAN = 32764;
sqlTypes.SQL_NULL = 32766;
})(sqlTypes || (exports.sqlTypes = sqlTypes = {}));
/** DPB constants. */
var dpb;
(function (dpb) {
/* tslint:disable */
dpb.version1 = 1;
dpb.lc_ctype = 48;
dpb.force_write = 24;
dpb.user_name = 28;
dpb.password = 29;
dpb.sql_role_name = 60;
/* tslint:enable */
})(dpb || (exports.dpb = dpb = {}));
/** TPB constants. */
var tpb;
(function (tpb) {
/* tslint:disable */
tpb.version1 = 1;
tpb.consistency = 1;
tpb.concurrency = 2;
tpb.wait = 6;
tpb.nowait = 7;
tpb.read = 8;
tpb.write = 9;
tpb.ignore_limbo = 14;
tpb.read_committed = 15;
tpb.autocommit = 16;
tpb.rec_version = 17;
tpb.no_rec_version = 18;
tpb.restart_requests = 19;
tpb.no_auto_undo = 20;
/* tslint:enable */
})(tpb || (exports.tpb = tpb = {}));
/** BPB constants. */
var bpb;
(function (bpb) {
/* tslint:disable */
bpb.version1 = 1;
bpb.source_type = 1;
bpb.target_type = 2;
bpb.type = 3;
bpb.source_interp = 4;
bpb.target_interp = 5;
bpb.filter_parameter = 6;
bpb.storage = 7;
bpb.type_segmented = 0x0;
bpb.type_stream = 0x1;
bpb.storage_main = 0x0;
bpb.storage_temp = 0x2;
/* tslint:enable */
})(bpb || (exports.bpb = bpb = {}));
/** EPB constants. */
var epb;
(function (epb) {
/* tslint:disable */
epb.version1 = 1;
/* tslint:enable */
})(epb || (exports.epb = epb = {}));
/** Blob info. */
var blobInfo;
(function (blobInfo) {
blobInfo.totalLength = 6;
})(blobInfo || (exports.blobInfo = blobInfo = {}));
/** Statement info. */
var statementInfo;
(function (statementInfo) {
statementInfo.sqlExecPathBlrText = 32;
})(statementInfo || (exports.statementInfo = statementInfo = {}));
/** Common info. */
var commonInfo;
(function (commonInfo) {
commonInfo.end = 1;
commonInfo.truncated = 2;
commonInfo.error = 3;
commonInfo.dataNotReady = 4;
commonInfo.length = 126;
commonInfo.flagEnd = 127;
})(commonInfo || (exports.commonInfo = commonInfo = {}));
var cancelType;
(function (cancelType) {
cancelType.disable = 1;
cancelType.enable = 2;
cancelType.raise = 3;
cancelType.abort = 4;
})(cancelType || (exports.cancelType = cancelType = {}));
var charSets;
(function (charSets) {
charSets.ascii = 2;
})(charSets || (exports.charSets = charSets = {}));
function createDpb(options) {
const code = (c) => String.fromCharCode(c);
const charSet = 'utf8';
let ret = `${code(dpb.version1)}${code(dpb.lc_ctype)}${code(charSet.length)}${charSet}`;
if (!options)
options = {};
if (!options.username)
options.username = process.env.ISC_USER;
if (!options.password)
options.password = process.env.ISC_PASSWORD;
if (options.username)
ret += `${code(dpb.user_name)}${code(options.username.length)}${options.username}`;
if (options.password)
ret += `${code(dpb.password)}${code(options.password.length)}${options.password}`;
if (options.role)
ret += `${code(dpb.sql_role_name)}${code(options.role.length)}${options.role}`;
const createOptions = options;
if (createOptions.forcedWrite != undefined)
ret += `${code(dpb.force_write)}${code(1)}${code(createOptions.forcedWrite ? 1 : 0)}`;
return Buffer.from(ret);
}
function createTpb(options) {
const code = (c) => String.fromCharCode(c);
let ret = code(tpb.version1);
if (!options)
options = {};
switch (options.accessMode) {
case 'READ_ONLY':
ret += code(tpb.read);
break;
case 'READ_WRITE':
ret += code(tpb.write);
break;
}
switch (options.waitMode) {
case 'NO_WAIT':
ret += code(tpb.nowait);
break;
case 'WAIT':
ret += code(tpb.wait);
break;
}
switch (options.isolation) {
case __1.TransactionIsolation.CONSISTENCY:
ret += code(tpb.consistency);
break;
case __1.TransactionIsolation.SNAPSHOT:
ret += code(tpb.concurrency);
break;
case __1.TransactionIsolation.READ_COMMITTED:
ret += code(tpb.read_committed) +
code(options.readCommittedMode == 'RECORD_VERSION' ? tpb.rec_version : tpb.no_rec_version);
break;
}
if (options.noAutoUndo)
ret += code(tpb.no_auto_undo);
if (options.ignoreLimbo)
ret += code(tpb.ignore_limbo);
if (options.restartRequests)
ret += code(tpb.restart_requests);
if (options.autoCommit)
ret += code(tpb.autocommit);
return Buffer.from(ret);
}
function createBpb(options) {
const code = (c) => String.fromCharCode(c);
let ret = code(bpb.version1);
if (!options)
options = {};
switch (options.type) {
case 'SEGMENTED':
ret += `${code(bpb.type)}${code(1)}${code(bpb.type_segmented)}`;
break;
case 'STREAM':
ret += `${code(bpb.type)}${code(1)}${code(bpb.type_stream)}`;
break;
}
return Buffer.from(ret);
}
/** Changes a number from a scale to another. */
/***
export function changeScale(value: number, inputScale: number, outputScale: number): number {
outputScale -= inputScale;
Math.pow(10, outputScale);
if (outputScale === 0)
return value;
else if (outputScale > 0)
return value / Math.pow(10, outputScale);
else // outputScale < 0
return value * Math.pow(10, -outputScale);
}
***/
/** Emulate Firebird isc_portable_integer. */
function getPortableInteger(buffer, length) {
if (!buffer || length <= 0 || length > 8)
return 0;
let value = 0;
let pos = 0;
for (let shift = 0; --length >= 0; shift += 8)
value += buffer[pos++] << shift;
return value;
}
/** Creates a data reader. */
function createDataReader(descriptors) {
const mappers = new Array(descriptors.length);
for (let i = 0; i < descriptors.length; ++i) {
const descriptor = descriptors[i];
mappers[i] = async (attachment, transaction, buffer) => {
const dataView = new DataView(buffer.buffer);
if (dataView.getInt16(descriptor.nullOffset, littleEndian) == -1)
return null;
switch (descriptor.type) {
// SQL_TEXT is handled changing its descriptor to SQL_VARYING with IMetadataBuilder.
case sqlTypes.SQL_VARYING: {
//// TODO: none, octets
const varLength = dataView.getUint16(descriptor.offset, littleEndian);
const decoder = new stringDecoder.StringDecoder('utf8');
const buf = Buffer.from(buffer.buffer, descriptor.offset + 2, varLength);
return decoder.end(buf);
}
/***
case sqlTypes.SQL_SHORT:
return changeScale(dataView.getInt16(descriptor.offset, littleEndian), descriptor.scale, 0);
case sqlTypes.SQL_LONG:
return changeScale(dataView.getInt32(descriptor.offset, littleEndian), descriptor.scale, 0);
//// TODO: sqlTypes.SQL_INT64
case sqlTypes.SQL_FLOAT:
return dataView.getFloat32(descriptor.offset, littleEndian);
***/
case sqlTypes.SQL_DOUBLE:
return dataView.getFloat64(descriptor.offset, littleEndian);
case sqlTypes.SQL_TYPE_TIME: {
const now = new Date();
const decodedTime = (0, date_time_1.decodeTime)(dataView.getUint32(descriptor.offset, littleEndian));
return new Date(now.getFullYear(), now.getMonth(), now.getDate(), decodedTime.hours, decodedTime.minutes, decodedTime.seconds, decodedTime.fractions / 10);
}
case sqlTypes.SQL_TIME_TZ_EX: {
const now = new Date();
const decodedTime = (0, date_time_1.decodeTime)(dataView.getUint32(descriptor.offset, littleEndian));
const date = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate(), decodedTime.hours, decodedTime.minutes, decodedTime.seconds, decodedTime.fractions / 10));
const timeZone = (0, time_zones_1.tzIdToString)(dataView.getUint16(descriptor.offset + 4, littleEndian));
const offset = dataView.getInt16(descriptor.offset + 6, littleEndian);
return {
date,
timeZone,
offset
};
}
case sqlTypes.SQL_TYPE_DATE: {
const decodedDate = (0, date_time_1.decodeDate)(dataView.getInt32(descriptor.offset, littleEndian));
if (decodedDate.year >= 100)
return new Date(decodedDate.year, decodedDate.month - 1, decodedDate.day);
else {
const date = new Date(2000, decodedDate.month - 1, decodedDate.day);
date.setFullYear(decodedDate.year);
return date;
}
}
case sqlTypes.SQL_TIMESTAMP: {
const decodedDate = (0, date_time_1.decodeDate)(dataView.getInt32(descriptor.offset, littleEndian));
const decodedTime = (0, date_time_1.decodeTime)(dataView.getUint32(descriptor.offset + 4, littleEndian));
if (decodedDate.year >= 100) {
return new Date(decodedDate.year, decodedDate.month - 1, decodedDate.day, decodedTime.hours, decodedTime.minutes, decodedTime.seconds, decodedTime.fractions / 10);
}
else {
const date = new Date(2000, decodedDate.month - 1, decodedDate.day, decodedTime.hours, decodedTime.minutes, decodedTime.seconds, decodedTime.fractions / 10);
date.setFullYear(decodedDate.year);
return date;
}
}
case sqlTypes.SQL_TIMESTAMP_TZ_EX: {
const decodedDate = (0, date_time_1.decodeDate)(dataView.getInt32(descriptor.offset, littleEndian));
const decodedTime = (0, date_time_1.decodeTime)(dataView.getUint32(descriptor.offset + 4, littleEndian));
const timeZone = (0, time_zones_1.tzIdToString)(dataView.getUint16(descriptor.offset + 8, littleEndian));
const offset = dataView.getInt16(descriptor.offset + 10, littleEndian);
let date;
if (decodedDate.year >= 100) {
date = new Date(Date.UTC(decodedDate.year, decodedDate.month - 1, decodedDate.day, decodedTime.hours, decodedTime.minutes, decodedTime.seconds, decodedTime.fractions / 10));
}
else {
date = new Date(Date.UTC(2000, decodedDate.month - 1, decodedDate.day, decodedTime.hours, decodedTime.minutes, decodedTime.seconds, decodedTime.fractions / 10));
date.setUTCFullYear(decodedDate.year);
}
return {
date,
timeZone,
offset
};
}
case sqlTypes.SQL_BOOLEAN:
return dataView.getInt8(descriptor.offset) != 0;
case sqlTypes.SQL_BLOB:
return new __1.Blob(attachment, buffer.slice(descriptor.offset, descriptor.offset + 8));
case sqlTypes.SQL_NULL:
return null;
default:
throw new Error(`Unrecognized Firebird type number ${descriptor.type}`);
}
};
}
return async (attachment, transaction, buffer) => {
return await Promise.all(mappers.map(mapper => mapper(attachment, transaction, buffer)));
};
}
/** Creates a data writer. */
function createDataWriter(descriptors) {
const mappers = new Array(descriptors.length);
for (let i = 0; i < descriptors.length; ++i) {
const descriptor = descriptors[i];
mappers[i] = async (attachment, transaction, buffer, value) => {
const dataView = new DataView(buffer.buffer);
if (value == null) {
dataView.setInt16(descriptor.nullOffset, -1, littleEndian);
return;
}
dataView.setInt16(descriptor.nullOffset, 0, littleEndian);
switch (descriptor.type) {
// SQL_TEXT is handled changing its descriptor to SQL_VARYING with IMetadataBuilder.
case sqlTypes.SQL_VARYING: {
//// TODO: none, octets
const str = value;
const strBuffer = Buffer.from(str);
const bytesArray = Uint8Array.from(strBuffer);
if (bytesArray.length > descriptor.length) {
throw new Error(`Length in bytes of string '${str}' (${bytesArray.length}) is ` +
`greater than maximum expect length ${descriptor.length}.`);
}
dataView.setUint16(descriptor.offset, bytesArray.length, littleEndian);
for (let j = 0; j < bytesArray.length; ++j)
buffer[descriptor.offset + 2 + j] = bytesArray[j];
break;
}
/***
case sqlTypes.SQL_SHORT:
dataView.setInt16(descriptor.offset, changeScale(value, 0, descriptor.scale), littleEndian);
break;
case sqlTypes.SQL_LONG:
dataView.setInt32(descriptor.offset, changeScale(value, 0, descriptor.scale), littleEndian);
break;
//// TODO: sqlTypes.SQL_INT64
case sqlTypes.SQL_FLOAT:
dataView.setFloat32(descriptor.offset, value, littleEndian);
break;
***/
case sqlTypes.SQL_DOUBLE:
dataView.setFloat64(descriptor.offset, value, littleEndian);
break;
case sqlTypes.SQL_TYPE_TIME: {
const date = value;
dataView.setUint32(descriptor.offset, (0, date_time_1.encodeTime)(date.getHours(), date.getMinutes(), date.getSeconds(), date.getMilliseconds() * 10), littleEndian);
break;
}
case sqlTypes.SQL_TIME_TZ:
case sqlTypes.SQL_TIME_TZ_EX: {
const zonedDate = value;
dataView.setUint32(descriptor.offset, (0, date_time_1.encodeTime)(zonedDate.date.getUTCHours(), zonedDate.date.getUTCMinutes(), zonedDate.date.getUTCSeconds(), zonedDate.date.getUTCMilliseconds() * 10), littleEndian);
dataView.setUint16(descriptor.offset + 4, (0, time_zones_1.tzStringToId)(zonedDate.timeZone), littleEndian);
break;
}
case sqlTypes.SQL_TYPE_DATE: {
const date = value;
dataView.setInt32(descriptor.offset, (0, date_time_1.encodeDate)(date.getFullYear(), date.getMonth() + 1, date.getDate()), littleEndian);
break;
}
case sqlTypes.SQL_TIMESTAMP: {
const date = value;
dataView.setInt32(descriptor.offset, (0, date_time_1.encodeDate)(date.getFullYear(), date.getMonth() + 1, date.getDate()), littleEndian);
dataView.setUint32(descriptor.offset + 4, (0, date_time_1.encodeTime)(date.getHours(), date.getMinutes(), date.getSeconds(), date.getMilliseconds() * 10), littleEndian);
break;
}
case sqlTypes.SQL_TIMESTAMP_TZ:
case sqlTypes.SQL_TIMESTAMP_TZ_EX: {
const zonedDate = value;
dataView.setInt32(descriptor.offset, (0, date_time_1.encodeDate)(zonedDate.date.getUTCFullYear(), zonedDate.date.getUTCMonth() + 1, zonedDate.date.getUTCDate()), littleEndian);
dataView.setUint32(descriptor.offset + 4, (0, date_time_1.encodeTime)(zonedDate.date.getUTCHours(), zonedDate.date.getUTCMinutes(), zonedDate.date.getUTCSeconds(), zonedDate.date.getUTCMilliseconds() * 10), littleEndian);
dataView.setUint16(descriptor.offset + 8, (0, time_zones_1.tzStringToId)(zonedDate.timeZone), littleEndian);
break;
}
case sqlTypes.SQL_BOOLEAN:
dataView.setInt8(descriptor.offset, value ? 1 : 0);
break;
case sqlTypes.SQL_BLOB:
{
const targetBlobId = buffer.subarray(descriptor.offset, descriptor.offset + 8);
if (value instanceof __1.BlobStream)
value = value.blob;
if (value instanceof Buffer) {
const blobStream = await attachment.createBlob(transaction);
try {
await blobStream.write(value);
}
catch (e) {
await blobStream.cancel();
throw e;
}
await blobStream.close();
targetBlobId.set(blobStream.blob.id);
}
else if (value instanceof __1.Blob) {
if (value.attachment == attachment)
targetBlobId.set(value.id);
else
throw new Error('Cannot pass a BLOB from another attachment as parameter.'); //// TODO: add support for it
}
else
throw new Error('Unrecognized type used as BLOB. Must be: Buffer or Blob.');
break;
}
case sqlTypes.SQL_NULL:
break;
default:
throw new Error(`Unrecognized Firebird type number ${descriptor.type}`);
}
};
}
return async (attachment, transaction, buffer, values) => {
if ((values || []).length !== descriptors.length)
throw new Error(`Incorrect number of parameters: expected ${descriptors.length}, received ${(values || []).length}.`);
await Promise.all(mappers.map((mapper, index) => mapper(attachment, transaction, buffer, values[index])));
};
}
//# sourceMappingURL=fb-util.js.map