Comparing version 0.0.5 to 0.1.0
@@ -22,4 +22,2 @@ // Copyright 2013 SAP AG. | ||
var Statement = protocol.Statement; | ||
var ResultSet = protocol.ResultSet; | ||
var Lob = protocol.Lob; | ||
@@ -165,2 +163,15 @@ module.exports = Client; | ||
}; | ||
executeDirect(this._connection, defaults, command, options, cb); | ||
return this; | ||
}; | ||
Client.prototype.execute = function execute(command, options, cb) { | ||
var defaults = { | ||
autoFetch: false | ||
}; | ||
executeDirect(this._connection, defaults, command, options, cb); | ||
return this; | ||
}; | ||
function executeDirect(connection, defaults, command, options, cb) { | ||
if (util.isFunction(options)) { | ||
@@ -176,4 +187,4 @@ cb = options; | ||
} | ||
var result = new Result(this._connection, options); | ||
this._connection.executeDirect({ | ||
var result = new Result(connection, options); | ||
connection.executeDirect({ | ||
command: command | ||
@@ -183,3 +194,2 @@ }, function onreply(err, reply) { | ||
}); | ||
return this; | ||
}; | ||
} |
@@ -27,2 +27,3 @@ // Copyright 2013 SAP AG. | ||
exports.LobOptions = require('./LobOptions'); | ||
exports.LobSourceType = require('./LobSourceType'); | ||
exports.MessageType = require('./MessageType'); | ||
@@ -37,2 +38,3 @@ exports.ParameterMode = require('./ParameterMode'); | ||
exports.TypeCode = require('./TypeCode'); | ||
exports.ReadFunction = require('./ReadFunction'); | ||
@@ -39,0 +41,0 @@ invert('ConnectOption'); |
@@ -28,2 +28,3 @@ // Copyright 2013 SAP AG. | ||
var bignum = util.bignum; | ||
var debug = util.debuglog('hdb'); | ||
@@ -97,2 +98,3 @@ var MAX_PACKET_SIZE = Math.pow(2, 17); | ||
function cleanup() { | ||
debug('cleanup'); | ||
self._socket.removeListener('error', onerror); | ||
@@ -279,10 +281,12 @@ self._socket = undefined; | ||
if (this._queue.empty && !this._queue.busy) { | ||
return endSocket(this._socket); | ||
return closeConnection.call(this); | ||
} | ||
this._queue.once('drain', endSocket.bind(null, this._socket)); | ||
this._queue.once('drain', closeConnection.bind(this)); | ||
}; | ||
function endSocket(socket) { | ||
socket.readable = false; | ||
socket.end(); | ||
function closeConnection() { | ||
/* jshint validthis:true */ | ||
debug('close'); | ||
this._socket.readable = false; | ||
this._socket.end(); | ||
} | ||
@@ -293,2 +297,4 @@ | ||
debug('send', message); | ||
var state = this._state; | ||
@@ -322,3 +328,3 @@ var buffer = messageToBuffer(message); | ||
var buffer; | ||
if (util.isBuffer(message)) { | ||
if (Buffer.isBuffer(message)) { | ||
buffer = message; | ||
@@ -392,2 +398,3 @@ } else { | ||
} | ||
debug('receive', reply); | ||
cb(error, reply); | ||
@@ -394,0 +401,0 @@ } |
@@ -19,2 +19,3 @@ // Copyright 2013 SAP AG. | ||
var TypeCode = common.TypeCode; | ||
var LobOptions = common.LobOptions; | ||
var bignum = util.bignum; | ||
@@ -31,125 +32,33 @@ | ||
var buffers = []; | ||
var byteLength; | ||
var buffer, type, value; | ||
for (var i = 0; i < params.length; i++) { | ||
type = params[i].type; | ||
var offset = 0; | ||
var lobs = []; | ||
var data = []; | ||
var dataType, value, buffer, i; | ||
for (i = 0; i < params.length; i++) { | ||
dataType = DataType[params[i].type]; | ||
value = params[i].value; | ||
if (typeof value === 'undefined' || value === null) { | ||
buffer = new Buffer(1); | ||
buffer[0] = type | 0x80; | ||
buffers.push(buffer); | ||
buffer = writeNull(dataType); | ||
} else { | ||
switch (type) { | ||
case TypeCode.TINYINT: | ||
buffer = new Buffer(2); | ||
buffer[0] = type; | ||
buffer.writeInt8(value, 1); | ||
buffers.push(buffer); | ||
break; | ||
case TypeCode.SMALLINT: | ||
buffer = new Buffer(3); | ||
buffer[0] = type; | ||
buffer.writeInt16LE(value, 1); | ||
buffers.push(buffer); | ||
break; | ||
case TypeCode.INT: | ||
buffer = new Buffer(5); | ||
buffer[0] = type; | ||
buffer.writeInt32LE(value, 1); | ||
buffers.push(buffer); | ||
break; | ||
case TypeCode.BIGINT: | ||
buffer = new Buffer(9); | ||
buffer[0] = type; | ||
bignum.writeInt64LE(buffer, value, 1); | ||
buffers.push(buffer); | ||
break; | ||
case TypeCode.REAL: | ||
buffer = new Buffer(5); | ||
buffer[0] = type; | ||
buffer.writeFloatLE(value, 1); | ||
buffers.push(buffer); | ||
break; | ||
case TypeCode.DOUBLE: | ||
buffer = new Buffer(9); | ||
buffer[0] = type; | ||
buffer.writeDoubleLE(value, 1); | ||
buffers.push(buffer); | ||
break; | ||
case TypeCode.STRING: | ||
case TypeCode.NSTRING: | ||
case TypeCode.VARCHAR1: | ||
case TypeCode.VARCHAR2: | ||
case TypeCode.CHAR: | ||
case TypeCode.NCHAR: | ||
case TypeCode.NVARCHAR: | ||
case TypeCode.SHORTTEXT: | ||
case TypeCode.ALPHANUM: | ||
byteLength = Buffer.byteLength(value, 'utf-8'); | ||
if (byteLength <= 245) { | ||
buffer = new Buffer(2 + byteLength); | ||
buffer[0] = type; | ||
buffer[1] = byteLength; | ||
buffer.write(value, 2, byteLength, 'utf-8'); | ||
} else if (byteLength <= 32767) { | ||
buffer = new Buffer(4 + byteLength); | ||
buffer[0] = type; | ||
buffer[1] = 246; | ||
buffer.writeInt16LE(byteLength, 2); | ||
buffer.write(value, 4, byteLength, 'utf-8'); | ||
} else { | ||
buffer = new Buffer(6 + byteLength); | ||
buffer[0] = type; | ||
buffer[1] = 247; | ||
buffer.writeInt32LE(byteLength, 2); | ||
buffer.write(value, 6, byteLength, 'utf-8'); | ||
} | ||
buffers.push(buffer); | ||
break; | ||
case TypeCode.BSTRING: | ||
case TypeCode.BINARY: | ||
case TypeCode.VARBINARY: | ||
byteLength = value.length; | ||
if (byteLength <= 245) { | ||
buffer = new Buffer(2 + byteLength); | ||
buffer[0] = type; | ||
buffer[1] = byteLength; | ||
value.copy(buffer, 2); | ||
} else if (byteLength <= 32767) { | ||
buffer = new Buffer(4 + byteLength); | ||
buffer[0] = type; | ||
buffer[1] = 246; | ||
buffer.writeInt16LE(byteLength, 2); | ||
value.copy(buffer, 4); | ||
} else { | ||
buffer = new Buffer(6 + byteLength); | ||
buffer[0] = type; | ||
buffer[1] = 247; | ||
buffer.writeInt32LE(byteLength, 2); | ||
value.copy(buffer, 6); | ||
} | ||
buffers.push(buffer); | ||
break; | ||
case TypeCode.DATE: | ||
buffer = new Buffer(6); | ||
buffer[0] = type; | ||
buffer.writeInt16LE(~~value.substring(0, 4), 1); | ||
buffer.writeInt8(~~value.substring(5, 7), 3); | ||
buffer.writeInt16LE(~~value.substring(8, 10), 4); | ||
buffers.push(buffer); | ||
break; | ||
case TypeCode.TIME: | ||
buffer = new Buffer(5); | ||
buffer[0] = type; | ||
buffer.writeInt8(~~value.substring(0, 2), 1); | ||
buffer.writeInt8(~~value.substring(3, 5), 2); | ||
buffer.writeUInt16LE(Math.round(parseFloat(value.substring(6))), 3); | ||
buffers.push(buffer); | ||
break; | ||
buffer = dataType.write(value); | ||
if (dataType === BLOB || dataType === NCLOB) { | ||
lobs.push({ | ||
index: i, | ||
buffer: value | ||
}); | ||
} | ||
} | ||
offset += buffer.length; | ||
data.push(buffer); | ||
} | ||
for (i = 0; i < lobs.length; i++) { | ||
// update position of lob in part | ||
data[lobs[i].index].writeInt32LE(offset + 1, 6); | ||
// append lob to part data | ||
buffer = lobs[i].buffer; | ||
offset += buffer.length; | ||
data.push(buffer); | ||
} | ||
part.argumentCount = getArgumentCount(params); | ||
part.buffer = Buffer.concat(buffers); | ||
part.buffer = Buffer.concat(data, offset); | ||
return part; | ||
@@ -161,2 +70,403 @@ } | ||
return 1; | ||
} | ||
var REGEX = { | ||
DATE: /(\d{4})-(\d{2})-(\d{2})/, | ||
TIME: /(\d{2}):(\d{2}):(\d{2}(?:\.\d+)?)/, | ||
TIMESTAMP: /(\d{4})-(\d{2})-(\d{2})[T ](\d{2}):(\d{2}):(\d{2}(?:\.\d+)?)/, | ||
DECIMAL: /^([+-])?(\d+)(?:\.(\d+))?(?:e([+-]?\d+))?$/ | ||
}; | ||
function writeNull(dataType) { | ||
return new Buffer([dataType.type | 0x80]); | ||
} | ||
function writeTinyInt(value) { | ||
/* jshint validthis:true */ | ||
var buffer = new Buffer(2); | ||
buffer[0] = this.type; | ||
buffer.writeInt8(value, 1); | ||
return buffer; | ||
} | ||
function writeSmallInt(value) { | ||
/* jshint validthis:true */ | ||
var buffer = new Buffer(3); | ||
buffer[0] = this.type; | ||
buffer.writeInt16LE(value, 1); | ||
return buffer; | ||
} | ||
function writeInt(value) { | ||
/* jshint validthis:true */ | ||
var buffer = new Buffer(5); | ||
buffer[0] = this.type; | ||
buffer.writeInt32LE(value, 1); | ||
return buffer; | ||
} | ||
function writeBigInt(value) { | ||
/* jshint validthis:true */ | ||
var buffer = new Buffer(9); | ||
buffer[0] = this.type; | ||
bignum.writeInt64LE(buffer, value, 1); | ||
return buffer; | ||
} | ||
function writeReal(value) { | ||
/* jshint validthis:true */ | ||
var buffer = new Buffer(5); | ||
buffer[0] = this.type; | ||
buffer.writeFloatLE(value, 1); | ||
return buffer; | ||
} | ||
function writeDouble(value) { | ||
/* jshint validthis:true */ | ||
var buffer = new Buffer(9); | ||
buffer[0] = this.type; | ||
buffer.writeDoubleLE(value, 1); | ||
return buffer; | ||
} | ||
function writeDecimal(value) { | ||
/* jshint validthis:true */ | ||
var decimal; | ||
if (util.isString(value)) { | ||
decimal = stringToDecimal(value); | ||
} else if (util.isNumber(value)) { | ||
decimal = stringToDecimal(value.toExponential()); | ||
} else { | ||
throw createInputError('DECIMAL'); | ||
} | ||
var buffer = new Buffer(17); | ||
buffer[0] = this.type; | ||
bignum.writeDec128(buffer, decimal, 1); | ||
return buffer; | ||
} | ||
function writeString(value) { | ||
/* jshint validthis:true */ | ||
var length = Buffer.byteLength(value, 'utf8'); | ||
var buffer; | ||
if (length <= 245) { | ||
buffer = new Buffer(2 + length); | ||
buffer[0] = this.type; | ||
buffer[1] = length; | ||
buffer.write(value, 2, length, 'utf8'); | ||
} else if (length <= 32767) { | ||
buffer = new Buffer(4 + length); | ||
buffer[0] = this.type; | ||
buffer[1] = 246; | ||
buffer.writeInt16LE(length, 2); | ||
buffer.write(value, 4, length, 'utf8'); | ||
} else { | ||
buffer = new Buffer(6 + length); | ||
buffer[0] = this.type; | ||
buffer[1] = 247; | ||
buffer.writeInt32LE(length, 2); | ||
buffer.write(value, 6, length, 'utf8'); | ||
} | ||
return buffer; | ||
} | ||
function writeBinary(value) { | ||
/* jshint validthis:true */ | ||
var length = value.length; | ||
var buffer; | ||
if (length <= 245) { | ||
buffer = new Buffer(2 + length); | ||
buffer[0] = this.type; | ||
buffer[1] = length; | ||
value.copy(buffer, 2); | ||
} else if (length <= 32767) { | ||
buffer = new Buffer(4 + length); | ||
buffer[0] = this.type; | ||
buffer[1] = 246; | ||
buffer.writeInt16LE(length, 2); | ||
value.copy(buffer, 4); | ||
} else { | ||
buffer = new Buffer(6 + length); | ||
buffer[0] = this.type; | ||
buffer[1] = 247; | ||
buffer.writeInt32LE(length, 2); | ||
value.copy(buffer, 6); | ||
} | ||
return buffer; | ||
} | ||
function writeLob(value) { | ||
/* jshint validthis:true, bitwise:false */ | ||
var buffer = new Buffer(10); | ||
buffer[0] = this.type; | ||
buffer[1] = LobOptions.DATA_INCLUDED | LobOptions.LAST_DATA; | ||
buffer.writeInt32LE(value.length, 2); | ||
return buffer; | ||
} | ||
function writeTime(value) { | ||
/* jshint validthis:true, bitwise:false */ | ||
var hours, minutes, milliseconds; | ||
if (util.isString(value)) { | ||
var time = value.match(REGEX.TIME); | ||
if (!time) { | ||
throw createInputError('TIME'); | ||
} | ||
hours = ~~time[1]; | ||
minutes = ~~time[2]; | ||
milliseconds = Math.floor(time[3] * 1000); | ||
} else { | ||
throw createInputError('TIME'); | ||
} | ||
var buffer = new Buffer(5); | ||
buffer[0] = this.type; | ||
buffer[1] = hours | 0x80; | ||
buffer[2] = minutes; | ||
buffer.writeUInt16LE(milliseconds, 3); | ||
return buffer; | ||
} | ||
function writeDate(value) { | ||
/* jshint validthis:true, bitwise:false */ | ||
var year, month, day; | ||
if (util.isString(value)) { | ||
var date = value.match(REGEX.DATE); | ||
if (!date) { | ||
throw createInputError('DATE'); | ||
} | ||
year = ~~date[1]; | ||
month = ~~date[2] - 1; | ||
day = ~~date[3]; | ||
} else { | ||
throw createInputError('DATE'); | ||
} | ||
var buffer = new Buffer(5); | ||
buffer[0] = this.type; | ||
buffer.writeUInt16LE(year, 1); | ||
buffer[2] |= 0x80; | ||
buffer[3] = month; | ||
buffer[4] = day; | ||
return buffer; | ||
} | ||
function writeTimestamp(value) { | ||
/* jshint validthis:true, bitwise:false */ | ||
var year, month, day, hours, minutes, milliseconds; | ||
if (util.isString(value)) { | ||
var ts = value.match(REGEX.TIMESTAMP); | ||
if (!ts) { | ||
throw createInputError('TIMESTAMP'); | ||
} | ||
year = ~~ts[1]; | ||
month = ~~ts[2] - 1; | ||
day = ~~ts[3]; | ||
hours = ~~ts[4]; | ||
minutes = ~~ts[5]; | ||
milliseconds = Math.floor(ts[6] * 1000); | ||
} else { | ||
throw createInputError('TIMESTAMP'); | ||
} | ||
var buffer = new Buffer(9); | ||
buffer[0] = this.type; | ||
buffer.writeUInt16LE(year, 1); | ||
buffer[2] |= 0x80; | ||
buffer[3] = month; | ||
buffer[4] = day; | ||
buffer[5] = hours | 0x80; | ||
buffer[6] = minutes; | ||
buffer.writeUInt16LE(milliseconds, 7); | ||
return buffer; | ||
} | ||
function writeDayDate(value) { | ||
/* jshint validthis:true, unused:false */ | ||
throw createNotImplementedError(); | ||
} | ||
function writeSecondTime(value) { | ||
/* jshint validthis:true, unused:false */ | ||
throw createNotImplementedError(); | ||
} | ||
function writeLongDate(value) { | ||
/* jshint validthis:true, unused:false */ | ||
throw createNotImplementedError(); | ||
} | ||
function writeSecondDate(value) { | ||
/* jshint validthis:true, unused:false */ | ||
throw createNotImplementedError(); | ||
} | ||
var TINYINT = { | ||
type: TypeCode.TINYINT, | ||
write: writeTinyInt | ||
}; | ||
var SMALLINT = { | ||
type: TypeCode.SMALLINT, | ||
write: writeSmallInt | ||
}; | ||
var INT = { | ||
type: TypeCode.INT, | ||
write: writeInt | ||
}; | ||
var BIGINT = { | ||
type: TypeCode.BIGINT, | ||
write: writeBigInt | ||
}; | ||
var STRING = { | ||
type: TypeCode.STRING, | ||
write: writeString | ||
}; | ||
var NSTRING = { | ||
type: TypeCode.NSTRING, | ||
write: writeString | ||
}; | ||
var BINARY = { | ||
type: TypeCode.BINARY, | ||
write: writeBinary | ||
}; | ||
var TIME = { | ||
type: TypeCode.TIME, | ||
write: writeTime | ||
}; | ||
var DATE = { | ||
type: TypeCode.DATE, | ||
write: writeDate | ||
}; | ||
var TIMESTAMP = { | ||
type: TypeCode.TIMESTAMP, | ||
write: writeTimestamp | ||
}; | ||
var BLOB = { | ||
type: TypeCode.BLOB, | ||
write: writeLob | ||
}; | ||
var NCLOB = { | ||
type: TypeCode.NCLOB, | ||
write: writeLob | ||
}; | ||
var DOUBLE = { | ||
type: TypeCode.DOUBLE, | ||
write: writeDouble | ||
}; | ||
var REAL = { | ||
type: TypeCode.REAL, | ||
write: writeReal | ||
}; | ||
var DECIMAL = { | ||
type: TypeCode.DECIMAL, | ||
write: writeDecimal | ||
}; | ||
var DAYDATE = { | ||
type: TypeCode.DAYDATE, | ||
write: writeDayDate | ||
}; | ||
var SECONDTIME = { | ||
type: TypeCode.SECONDTIME, | ||
write: writeSecondTime | ||
}; | ||
var LONGDATE = { | ||
type: TypeCode.LONGDATE, | ||
write: writeLongDate | ||
}; | ||
var SECONDDATE = { | ||
type: TypeCode.SECONDDATE, | ||
write: writeSecondDate | ||
}; | ||
var DataType = {}; | ||
// TinyInt | ||
DataType[TypeCode.TINYINT] = TINYINT; | ||
// SmallInt | ||
DataType[TypeCode.SMALLINT] = SMALLINT; | ||
// Int | ||
DataType[TypeCode.INT] = INT; | ||
// BigInt | ||
DataType[TypeCode.BIGINT] = BIGINT; | ||
// Double | ||
DataType[TypeCode.DOUBLE] = DOUBLE; | ||
// Real | ||
DataType[TypeCode.REAL] = REAL; | ||
// Decimal | ||
DataType[TypeCode.DECIMAL] = DECIMAL; | ||
// String | ||
DataType[TypeCode.STRING] = STRING; | ||
DataType[TypeCode.VARCHAR1] = STRING; | ||
DataType[TypeCode.VARCHAR2] = STRING; | ||
DataType[TypeCode.CHAR] = STRING; | ||
DataType[TypeCode.SHORTTEXT] = STRING; | ||
DataType[TypeCode.ALPHANUM] = STRING; | ||
// NString | ||
DataType[TypeCode.NCHAR] = NSTRING; | ||
DataType[TypeCode.NVARCHAR] = NSTRING; | ||
DataType[TypeCode.NSTRING] = NSTRING; | ||
// Binary | ||
DataType[TypeCode.BINARY] = BINARY; | ||
DataType[TypeCode.VARBINARY] = BINARY; | ||
DataType[TypeCode.BSTRING] = BINARY; | ||
// BLob | ||
DataType[TypeCode.BLOB] = BLOB; | ||
DataType[TypeCode.LOCATOR] = BLOB; | ||
// NCLob | ||
DataType[TypeCode.CLOB] = NCLOB; | ||
DataType[TypeCode.NCLOB] = NCLOB; | ||
DataType[TypeCode.NLOCATOR] = NCLOB; | ||
DataType[TypeCode.TEXT] = NCLOB; | ||
// Date | ||
DataType[TypeCode.DATE] = DATE; | ||
// Time | ||
DataType[TypeCode.TIME] = TIME; | ||
// Timestamp | ||
DataType[TypeCode.TIMESTAMP] = TIMESTAMP; | ||
// DayDate | ||
DataType[TypeCode.DAYDATE] = DAYDATE; | ||
// SecondTime | ||
DataType[TypeCode.SECONDTIME] = SECONDTIME; | ||
// LongDate | ||
DataType[TypeCode.LONGDATE] = LONGDATE; | ||
// SecondDate | ||
DataType[TypeCode.SECONDDATE] = SECONDDATE; | ||
function stringToDecimal(str) { | ||
/* jshint bitwise:false */ | ||
var dec = str.match(REGEX.DECIMAL); | ||
if (!dec) { | ||
throw createInputError('DECIMAL'); | ||
} | ||
var sign = dec[1] === '-' ? -1 : 1; | ||
var mInt = dec[2] || '0'; | ||
var mFrac = dec[3] || ''; | ||
var exp = ~~dec[4]; | ||
return { | ||
s: sign, | ||
m: mInt + mFrac, | ||
e: exp - mFrac.length | ||
}; | ||
} | ||
function createInputError(type) { | ||
return new Error(util.format('Wrong input for %s type', type)); | ||
} | ||
function createNotImplementedError() { | ||
return new Error('Not implemented yet'); | ||
} |
@@ -60,3 +60,9 @@ // Copyright 2013 SAP AG. | ||
util.inherits(SqlError, Error); | ||
function SqlError() { | ||
Error.call(this); | ||
if (Error.captureStackTrace) { | ||
Error.captureStackTrace(this, SqlError); | ||
} | ||
this.message = undefined; | ||
@@ -63,0 +69,0 @@ this.code = undefined; |
@@ -18,3 +18,3 @@ // Copyright 2013 SAP AG. | ||
var EventEmitter = require('events').EventEmitter; | ||
var Readable = require('stream').Readable; | ||
var debug = util.debuglog('hdb_lob'); | ||
@@ -25,158 +25,75 @@ module.exports = Lob; | ||
Lob.DEFAULT_READ_SIZE = Math.pow(2, 14); | ||
Lob.DEFAULT_READ_SIZE = Math.pow(2, 17); | ||
Lob.MAX_READ_SIZE = Math.pow(2, 18); | ||
Lob.TYPE_BLOB = 0; | ||
Lob.TYPE_CLOB = 1; | ||
function Lob(connection, ld) { | ||
function Lob(readLob, ld, options) { | ||
EventEmitter.call(this); | ||
this._connection = connection; | ||
// public | ||
this.locatorId = ld.locatorId; | ||
this.finished = false; | ||
// private | ||
this._readLob = readLob; | ||
this._running = undefined; | ||
this._readSize = Lob.DEFAULT_READ_SIZE; | ||
this.type = ld.type; | ||
this.id = ld.locatorId; | ||
this.byteLength = ld.byteLength; | ||
this.charLength = ld.charLength; | ||
this.finished = false; | ||
this.offset = 0; | ||
this._data = null; | ||
if (Buffer.isBuffer(ld.chunk) && ld.chunk.length) { | ||
var size = ld.chunk.length; | ||
if (this.type === Lob.TYPE_CLOB) { | ||
size = ld.chunk.toString('utf-8').length; | ||
} | ||
this._data = { | ||
isLast: ld.isLast, | ||
chunk: ld.chunk, | ||
size: size | ||
}; | ||
this._offset = 0; | ||
options = options || {}; | ||
this._readSize = options.readSize || Lob.DEFAULT_READ_SIZE; | ||
this._data = undefined; | ||
if (ld.chunk) { | ||
this._data = ld; | ||
} | ||
} | ||
Lob.create = function createLob(connection, ld) { | ||
return new Lob(connection, ld); | ||
Lob.prototype.pause = function pause() { | ||
this._running = false; | ||
}; | ||
Object.defineProperties(Lob.prototype, { | ||
readSize: { | ||
get: function getFetchSize() { | ||
return this._readSize; | ||
} | ||
Lob.prototype.resume = function resume() { | ||
if (this._running || this.finished) { | ||
return; | ||
} | ||
}); | ||
Lob.prototype.setReadSize = function setReadSize(readSize) { | ||
if (readSize > Lob.MAX_READ_SIZE) { | ||
this._readSize = Lob.MAX_READ_SIZE; | ||
this._running = true; | ||
if (util.isObject(this._data)) { | ||
handleData.call(this, this._data); | ||
this._data = undefined; | ||
} else { | ||
sendReadLob.call(this); | ||
} | ||
this._readSize = readSize; | ||
return this; | ||
}; | ||
Lob.prototype.end = function end() { | ||
this.finished = true; | ||
this._running = false; | ||
this._connection = undefined; | ||
process.nextTick(emitEnd.bind(this)); | ||
}; | ||
function emitEnd() { | ||
/* jshint validthis:true */ | ||
debug('emit "end"'); | ||
this.emit('end'); | ||
} | ||
Lob.prototype.read = function read(cb) { | ||
if (typeof this._running !== 'undefined') { | ||
if (!util.isUndefined(this._running)) { | ||
var err = new Error('Lob invalid state error'); | ||
return cb(err); | ||
} | ||
readLob(this, cb); | ||
util.readData(this, cb); | ||
}; | ||
Lob.prototype.createReadStream = function createReadStream(options) { | ||
if (typeof this._running !== 'undefined') { | ||
if (!util.isUndefined(this._running)) { | ||
return null; | ||
} | ||
return createReadLobStream(this, options); | ||
}; | ||
Lob.prototype.stopRead = function stopRead() { | ||
this._running = false; | ||
return this; | ||
return util.createReadStream(this, ['error'], options); | ||
}; | ||
Lob.prototype.startRead = function startRead() { | ||
if (!this._running && !this.finished) { | ||
this._running = true; | ||
if (util.isObject(this._data)) { | ||
handleData.call(this, this._data); | ||
this._data = undefined; | ||
} else { | ||
sendReadLob.call(this); | ||
} | ||
} | ||
return this; | ||
}; | ||
function createReadLobStream(lob, options) { | ||
options = options || {}; | ||
var readable = new Readable(options); | ||
readable._read = function _read() { | ||
lob.startRead(); | ||
}; | ||
function cleanup() { | ||
lob.removeListener('error', onerror); | ||
lob.removeListener('data', ondata); | ||
lob.removeListener('end', onend); | ||
} | ||
function onerror(err) { | ||
cleanup(); | ||
readable.emit('error', err); | ||
} | ||
lob.on('error', onerror); | ||
function ondata(chunk) { | ||
if (!readable.push(chunk)) { | ||
lob.stopFetch(); | ||
} | ||
} | ||
lob.on('data', ondata); | ||
function onend() { | ||
cleanup(); | ||
readable.push(null); | ||
} | ||
lob.on('end', onend); | ||
return readable; | ||
} | ||
function readLob(lob, cb) { | ||
var offset = 0; | ||
var buffer = new Buffer(lob.byteLength); | ||
function done(err) { | ||
lob.removeListener('error', onerror); | ||
lob.removeListener('data', ondata); | ||
lob.removeListener('end', onend); | ||
if (util.isFunction(cb)) { | ||
cb(err, buffer); | ||
} | ||
} | ||
function onerror(err) { | ||
done(err); | ||
} | ||
lob.on('error', onerror); | ||
function ondata(chunk) { | ||
chunk.copy(buffer, offset); | ||
offset += chunk.length; | ||
} | ||
lob.on('data', ondata); | ||
function onend() { | ||
done(null); | ||
} | ||
lob.on('end', onend); | ||
lob.startRead(); | ||
} | ||
function sendReadLob() { | ||
/* jshint validthis:true */ | ||
this._connection.readLob({ | ||
locatorId: this.id, | ||
offset: this.offset + 1, | ||
debug('sendReadLob', this.locatorId); | ||
this._readLob({ | ||
locatorId: this.locatorId, | ||
offset: this._offset + 1, | ||
length: this._readSize | ||
@@ -188,3 +105,3 @@ }, receiveData.bind(this)); | ||
/* jshint validthis:true */ | ||
debug('receiveData()'); | ||
if (err) { | ||
@@ -206,5 +123,6 @@ this._running = false; | ||
/* jshint validthis:true */ | ||
var size = data.size || this._readSize; | ||
debug('handleData(%d)', size); | ||
if (Buffer.isBuffer(data.chunk)) { | ||
this.offset += data.size || this._readSize; | ||
this._offset += size; | ||
this.emit('data', data.chunk); | ||
@@ -214,6 +132,3 @@ } | ||
if (data.isLast) { | ||
this.finished = true; | ||
this._running = false; | ||
process.nextTick(this.emit.bind(this, 'end')); | ||
return; | ||
return this.end(); | ||
} | ||
@@ -220,0 +135,0 @@ |
@@ -16,426 +16,72 @@ // Copyright 2013 SAP AG. | ||
var util = require('../util'); | ||
var util = require('util'); | ||
var common = require('./common'); | ||
var bignum = util.bignum; | ||
var ResultSetTransform = require('./ResultSetTransform'); | ||
var Reader = require('./Reader'); | ||
var ReadFunction = common.ReadFunction; | ||
var TypeCode = common.TypeCode; | ||
var READ_TINYINT = 'readTinyInt()'; | ||
var READ_SMALLINT = 'readSmallInt()'; | ||
var READ_INT = 'readInt()'; | ||
var READ_BIGINT = 'readBigInt()'; | ||
var READ_STRING = 'readBytes(\'utf-8\')'; | ||
var READ_BINARY = 'readBytes()'; | ||
var READ_DATE = 'readDate()'; | ||
var READ_DAYDATE = 'readDayDate()'; | ||
var READ_TIME = 'readTime()'; | ||
var READ_SECONDTIME = 'readSecondTime()'; | ||
var READ_TIMESTAMP = 'readTimestamp()'; | ||
var READ_LONGDATE = 'readLongDate()'; | ||
var READ_SECONDDATE = 'readSecondDate()'; | ||
var READ_LOB = 'readLob(0)'; | ||
var READ_NCLOB = 'readLob(1)'; | ||
var READ_DOUBLE = 'readDouble()'; | ||
var READ_FLOAT = 'readFloat()'; | ||
var READ_DECIMAL = 'readDecimal(%d)'; | ||
var readFunctionMap = {}; | ||
readFunctionMap[TypeCode.TINYINT] = READ_TINYINT; | ||
readFunctionMap[TypeCode.SMALLINT] = READ_SMALLINT; | ||
readFunctionMap[TypeCode.INT] = READ_INT; | ||
readFunctionMap[TypeCode.BIGINT] = READ_BIGINT; | ||
readFunctionMap[TypeCode.STRING] = READ_STRING; | ||
readFunctionMap[TypeCode.VARCHAR1] = READ_STRING; | ||
readFunctionMap[TypeCode.VARCHAR2] = READ_STRING; | ||
readFunctionMap[TypeCode.CHAR] = READ_STRING; | ||
readFunctionMap[TypeCode.NCHAR] = READ_STRING; | ||
readFunctionMap[TypeCode.NVARCHAR] = READ_STRING; | ||
readFunctionMap[TypeCode.NSTRING] = READ_STRING; | ||
readFunctionMap[TypeCode.SHORTTEXT] = READ_STRING; | ||
readFunctionMap[TypeCode.ALPHANUM] = READ_STRING; | ||
readFunctionMap[TypeCode.BINARY] = READ_BINARY; | ||
readFunctionMap[TypeCode.VARBINARY] = READ_BINARY; | ||
readFunctionMap[TypeCode.BSTRING] = READ_BINARY; | ||
readFunctionMap[TypeCode.DATE] = READ_DATE; | ||
readFunctionMap[TypeCode.TIME] = READ_TIME; | ||
readFunctionMap[TypeCode.TIMESTAMP] = READ_TIMESTAMP; | ||
readFunctionMap[TypeCode.DAYDATE] = READ_DAYDATE; | ||
readFunctionMap[TypeCode.SECONDTIME] = READ_SECONDTIME; | ||
readFunctionMap[TypeCode.LONGDATE] = READ_LONGDATE; | ||
readFunctionMap[TypeCode.SECONDDATE] = READ_SECONDDATE; | ||
readFunctionMap[TypeCode.BLOB] = READ_LOB; | ||
readFunctionMap[TypeCode.LOCATOR] = READ_LOB; | ||
readFunctionMap[TypeCode.CLOB] = READ_NCLOB; | ||
readFunctionMap[TypeCode.NCLOB] = READ_NCLOB; | ||
readFunctionMap[TypeCode.NLOCATOR] = READ_NCLOB; | ||
readFunctionMap[TypeCode.TEXT] = READ_NCLOB; | ||
readFunctionMap[TypeCode.DOUBLE] = READ_DOUBLE; | ||
readFunctionMap[TypeCode.REAL] = READ_FLOAT; | ||
readFunctionMap[TypeCode.DECIMAL] = READ_DECIMAL; | ||
function createFunctionBody(metadata, nameProperty) { | ||
var functionBody = ['var obj = {};']; | ||
metadata.forEach(function (column, index) { | ||
var fn = readFunctionMap[column.dataType]; | ||
if (column.dataType === TypeCode.DECIMAL) { | ||
fn = util.format(fn, column.fraction); | ||
} | ||
var key = (typeof nameProperty === 'string') ? column[nameProperty] : | ||
index; | ||
functionBody.push('obj["' + key + '"] = this.' + fn + ';'); | ||
}); | ||
functionBody.push('return obj;'); | ||
return functionBody.join('\n'); | ||
} | ||
module.exports = Parser; | ||
Parser.DEFAULT_THRESHOLD = 128; | ||
function Parser(metadata, options) { | ||
/*jshint evil:true */ | ||
options = options || {}; | ||
if (typeof options.nameProperty === 'undefined') { | ||
options.nameProperty = 'columnDisplayName'; | ||
} | ||
this._parseRow = new Function(createFunctionBody(metadata, options.nameProperty)); | ||
this._threshold = options.threshold || Parser.DEFAULT_THRESHOLD; | ||
this._queue = []; | ||
this.taskId = 0; | ||
if (typeof setImmediate !== 'undefined') { | ||
this._setImmediate = setImmediate; | ||
} else { | ||
this._setImmediate = process.nextTick; | ||
} | ||
function Parser(metadata) { | ||
this.metadata = metadata; | ||
} | ||
Parser.prototype.parse = function parse(buffer, target, done) { | ||
var task = new ParserTask(buffer, target, done); | ||
task.id = ++this.taskId; | ||
if (this._queue.push(task) === 1) { | ||
executeTask(this); | ||
} | ||
Parser.create = function createParser(metadata) { | ||
return new Parser(metadata); | ||
}; | ||
Parser.prototype.parseParameters = function parseParameters(buffer) { | ||
return this._parseRow.call(new ParserState(buffer)); | ||
Parser.parseParameters = function parseParameters(metadata, buffer) { | ||
return Parser.create(metadata).parseParams(buffer); | ||
}; | ||
function executeTask(parser) { | ||
var task = parser._queue[0]; | ||
if (task.active) return; | ||
task.active = true; | ||
var state = task.state; | ||
var target = task.target; | ||
var parseRow = parser._parseRow.bind(state); | ||
var bufferLength = state.buffer.length; | ||
function read() { | ||
for (var i = 0; i < parser._threshold && state.offset < bufferLength; i++) { | ||
target.push(parseRow()); | ||
} | ||
return state.offset < bufferLength; | ||
} | ||
function done(err) { | ||
task.done(err); | ||
parser._queue.shift(); | ||
if (err) return; | ||
if (parser._queue.length) { | ||
executeTask(parser); | ||
} | ||
} | ||
function next() { | ||
parser._setImmediate(function () { | ||
var bytesRemaining; | ||
try { | ||
bytesRemaining = read(); | ||
} catch (err) { | ||
return done(err); | ||
} | ||
if (bytesRemaining > 0) { | ||
next(); | ||
} else { | ||
done(null); | ||
} | ||
}); | ||
} | ||
next(); | ||
} | ||
function ParserTask(buffer, target, callback) { | ||
this.state = new ParserState(buffer); | ||
this.active = false; | ||
var self = this; | ||
if (typeof target === 'function') { | ||
this.target = []; | ||
callback = target; | ||
this.done = function (err) { | ||
callback(err, self.target); | ||
}; | ||
} else { | ||
this.target = target; | ||
this.done = function (err) { | ||
callback(err); | ||
}; | ||
} | ||
} | ||
function ParserState(buffer) { | ||
this.buffer = buffer; | ||
this.offset = 0; | ||
} | ||
ParserState.prototype.readTinyInt = function () { | ||
if (this.buffer[this.offset++] === 0x00) { | ||
return null; | ||
} | ||
/* | ||
var uInt = this.buffer[this.offset++]; | ||
if (!(uInt & 0x80)) | ||
return uInt; | ||
return ((0xff - uInt + 1) * -1); | ||
*/ | ||
var value = this.buffer.readInt8(this.offset); | ||
this.offset += 1; | ||
return value; | ||
Parser.prototype.createParseRowFunction = function createParseRowFunction() { | ||
return this.createParseFunction('columnDisplayName'); | ||
}; | ||
ParserState.prototype.readSmallInt = function () { | ||
if (this.buffer[this.offset++] === 0x00) { | ||
return null; | ||
} | ||
var value = this.buffer.readInt16LE(this.offset); | ||
this.offset += 2; | ||
return value; | ||
Parser.prototype.createParseParamsFunction = function createParseParamsFunction() { | ||
return this.createParseFunction('name'); | ||
}; | ||
ParserState.prototype.readInt = function () { | ||
if (this.buffer[this.offset++] === 0x00) { | ||
return null; | ||
} | ||
var value = this.buffer.readInt32LE(this.offset); | ||
this.offset += 4; | ||
return value; | ||
Parser.prototype.createParseFunction = function createParseFunction(name) { | ||
/*jshint evil:true */ | ||
return new Function(createFunctionBody(this.metadata, name || | ||
'columnDisplayName')); | ||
}; | ||
ParserState.prototype.readBigInt = function () { | ||
if (this.buffer[this.offset++] === 0x00) { | ||
return null; | ||
} | ||
var value = bignum.readInt64LE(this.buffer, this.offset); | ||
this.offset += 8; | ||
return value; | ||
Parser.prototype.parseParams = function parseParams(buffer) { | ||
var reader = new Reader(buffer); | ||
var parseParamsFunction = this.createParseParamsFunction(); | ||
return parseParamsFunction.call(reader); | ||
}; | ||
ParserState.prototype.readString = function () { | ||
this.readBytes('utf-8'); | ||
}; | ||
ParserState.prototype.readBinary = function () { | ||
this.readBytes(); | ||
}; | ||
ParserState.prototype.readBytes = function (encoding) { | ||
var length = this.buffer[this.offset++]; | ||
switch (length) { | ||
case 0xff: | ||
return null; | ||
case 0xf6: | ||
length = this.buffer.readInt16LE(this.offset); | ||
this.offset += 2; | ||
break; | ||
case 0xf7: | ||
length = this.buffer.readInt32LE(this.offset); | ||
this.offset += 4; | ||
break; | ||
Parser.prototype.parse = function parse(buffer) { | ||
var reader = new Reader(buffer); | ||
var parseRow = this.createParseRowFunction().bind(reader); | ||
var rows = []; | ||
while (reader.hasMore()) { | ||
rows.push(parseRow()); | ||
} | ||
var value; | ||
if (encoding) { | ||
value = this.buffer.toString(encoding, this.offset, this.offset + length); | ||
} else { | ||
value = new Buffer(length); | ||
this.buffer.copy(value, 0, this.offset, this.offset + length); | ||
} | ||
this.offset += length; | ||
return value; | ||
return rows; | ||
}; | ||
ParserState.prototype.readDate = function () { | ||
if (!(this.buffer[this.offset + 1] & 0x80)) { | ||
this.offset += 4; | ||
return null; | ||
} | ||
var year = this.buffer.readInt16LE(this.offset); | ||
if (year & 0x8000) year = year & 0x7fff; | ||
if (year & 0x4000) year = year | 0x8000; | ||
var month = this.buffer.readInt8(this.offset + 2) + 1; | ||
var day = this.buffer.readInt8(this.offset + 3); | ||
this.offset += 4; | ||
return bignum.lpad4(year) + '-' + bignum.lpad2(month) + '-' + bignum.lpad2( | ||
day); | ||
Parser.prototype.createTransform = function createTransform(rs, options) { | ||
return new ResultSetTransform(this.createParseRowFunction(), rs, options); | ||
}; | ||
ParserState.prototype.readTime = function () { | ||
if (!(this.buffer[this.offset] & 0x80)) { | ||
this.offset += 4; | ||
return null; | ||
} | ||
var hour = this.buffer.readInt8(this.offset); | ||
if (hour & 0x80) hour = hour & 0x7f; | ||
var min = this.buffer.readInt8(this.offset + 1); | ||
var msec = this.buffer.readUInt16LE(this.offset + 2); | ||
this.offset += 4; | ||
return bignum.lpad2(hour) + ':' + bignum.lpad2(min) + ':' + bignum.lpad2(msec / | ||
1000); | ||
}; | ||
function createFunctionBody(metadata, nameProperty) { | ||
var functionBody = ['var obj = {};']; | ||
ParserState.prototype.readTimestamp = function () { | ||
var date = this.readDate(); | ||
var time = this.readTime(); | ||
if (!date && !time) { | ||
return null; | ||
} else if (date && time) { | ||
return date + 'T' + time; | ||
} else if (date) { | ||
return date; | ||
} else { | ||
return time; | ||
function addParseColumnLine(column, index) { | ||
var fn = ReadFunction[column.dataType]; | ||
if (column.dataType === TypeCode.DECIMAL) { | ||
fn = util.format(fn, column.fraction); | ||
} | ||
var key = (typeof nameProperty === 'string') ? column[nameProperty] : | ||
index; | ||
functionBody.push('obj["' + key + '"] = this.' + fn + ';'); | ||
} | ||
}; | ||
ParserState.prototype.readDayDate = function () { | ||
var value = this.buffer.readInt32LE(this.offset); | ||
this.offset += 4; | ||
if (value === 3652062 || value === 0) { | ||
return null; | ||
} | ||
return value - 1; | ||
}; | ||
ParserState.prototype.readSecondTime = function () { | ||
var value = this.buffer.readInt32LE(this.offset); | ||
this.offset += 4; | ||
if (value === 86402 || value === 0) { | ||
return null; | ||
} | ||
return value - 1; | ||
}; | ||
ParserState.prototype.readSecondDate = function () { | ||
var value = bignum.readInt64LE(this.buffer, this.offset); | ||
this.offset += 8; | ||
if (value === 315538070401) { | ||
return null; | ||
} | ||
return value - 1; | ||
}; | ||
ParserState.prototype.readLongDate = function () { | ||
var value = bignum.readInt64LE(this.buffer, this.offset); | ||
this.offset += 8; | ||
if (value === '3155380704000000001') { | ||
return null; | ||
} | ||
if (typeof value === 'string') { | ||
var index = value.length - 7; | ||
return value.substring(0, index) + lpad7(value.substring(index) - 1); | ||
} else { | ||
return value - 1; | ||
} | ||
}; | ||
ParserState.prototype.readLob = function readLob(type) { | ||
// skip source type | ||
this.offset += 1; | ||
// offset 1 | ||
var options = this.buffer[this.offset]; | ||
this.offset += 1; | ||
// offset 2 | ||
if ( !! (options & 0x01)) { | ||
return null; | ||
} | ||
// skip 2 byte filler | ||
this.offset += 2; | ||
// offset 4 | ||
var charLength = bignum.readInt64LE(this.buffer, this.offset); | ||
this.offset += 8; | ||
// offset 12 | ||
var byteLength = bignum.readInt64LE(this.buffer, this.offset); | ||
this.offset += 8; | ||
// offset 20 | ||
var locatorId = this.buffer.slice(this.offset, this.offset + 8); | ||
this.offset += 8; | ||
// offset 28 | ||
var chunkLength = this.buffer.readInt32LE(this.offset); | ||
this.offset += 4; | ||
// offset 32 | ||
var chunk = this.buffer.slice(this.offset, this.offset + chunkLength); | ||
this.offset += chunkLength; | ||
// is last | ||
var isLast = !! (options & 0x04); | ||
if (isLast) { | ||
return chunk; | ||
} | ||
return { | ||
type: type, | ||
locatorId: locatorId, | ||
charLength: charLength, | ||
byteLength: byteLength, | ||
isLast: isLast, | ||
chunk: chunk | ||
}; | ||
}; | ||
ParserState.prototype.readNCLob = function () { | ||
if (this.buffer[this.offset + 1] === 0x01) { | ||
this.offset += 2; | ||
return null; | ||
} | ||
var length = this.buffer.readInt32LE(this.offset + 28); | ||
this.offset += 32; | ||
var value = this.buffer.toString('utf-8', this.offset, this.offset + length); | ||
this.offset += length; | ||
return value; | ||
}; | ||
ParserState.prototype.readDouble = function () { | ||
if (this.buffer[this.offset] === 0xff && | ||
this.buffer[this.offset + 1] === 0xff && | ||
this.buffer[this.offset + 2] === 0xff && | ||
this.buffer[this.offset + 3] === 0xff && | ||
this.buffer[this.offset + 4] === 0xff && | ||
this.buffer[this.offset + 5] === 0xff && | ||
this.buffer[this.offset + 6] === 0xff && | ||
this.buffer[this.offset + 7] === 0xff) { | ||
this.offset += 8; | ||
return null; | ||
} | ||
var value = this.buffer.readDoubleLE(this.offset); | ||
this.offset += 8; | ||
return value; | ||
}; | ||
ParserState.prototype.readFloat = function () { | ||
if (this.buffer[this.offset] === 0xff && this.buffer[this.offset + 1] === | ||
0xff && this.buffer[this.offset + 2] === 0xff && this.buffer[this.offset + | ||
3] === 0xff) { | ||
this.offset += 4; | ||
return null; | ||
} | ||
var value = this.buffer.readFloatLE(this.offset); | ||
this.offset += 4; | ||
return value; | ||
}; | ||
ParserState.prototype.readDecimal = function (fraction) { | ||
var value; | ||
if (fraction > 34) { | ||
value = bignum.readDecFloat(this.buffer, this.offset); | ||
} else { | ||
value = bignum.readDecFixed(this.buffer, this.offset, fraction); | ||
} | ||
this.offset += 16; | ||
return value; | ||
}; | ||
metadata.forEach(addParseColumnLine); | ||
functionBody.push('return obj;'); | ||
return functionBody.join('\n'); | ||
} |
@@ -42,18 +42,20 @@ // Copyright 2013 SAP AG. | ||
Segment.prototype.add = function add(kind, options) { | ||
if (options) { | ||
var dataModule; | ||
if (util.isNumber(kind)) { | ||
dataModule = data[kind]; | ||
} else { | ||
dataModule = kind.module; | ||
kind = kind.kind; | ||
} | ||
var part = new Part({ | ||
kind: kind | ||
Segment.prototype.add = function add(kind, args) { | ||
if (!args) { | ||
return; | ||
} | ||
if (util.isNumber(kind)) { | ||
this.parts.push({ | ||
kind: kind, | ||
args: args | ||
}); | ||
part.argumentCount = dataModule.getArgumentCount(options); | ||
dataModule.write(part, options); | ||
this.parts.push(part); | ||
return; | ||
} | ||
if (util.isObject(kind)) { | ||
this.parts.push({ | ||
kind: kind.kind, | ||
module: kind.module, | ||
args: args | ||
}); | ||
} | ||
}; | ||
@@ -65,8 +67,10 @@ | ||
var length = SEGMENT_HEADER_LENGTH; | ||
var buffers = this.parts.map(function (part) { | ||
var buffer = part.toBuffer(remainingSize); | ||
var buffers = []; | ||
for (var i = 0; i < this.parts.length; i++) { | ||
this.parts[i] | ||
var buffer = partToBuffer(this.parts[i], remainingSize); | ||
remainingSize -= buffer.length; | ||
length += buffer.length; | ||
return buffer; | ||
}); | ||
buffers.push(buffer); | ||
}; | ||
@@ -95,2 +99,12 @@ var header = new Buffer(SEGMENT_HEADER_LENGTH); | ||
return Buffer.concat(buffers, length); | ||
}; | ||
}; | ||
function partToBuffer(pd, remainingSize) { | ||
var m = pd.module || data[pd.kind]; | ||
var part = new Part({ | ||
kind: pd.kind | ||
}); | ||
part.argumentCount = m.getArgumentCount(pd.args); | ||
m.write(part, pd.args, remainingSize); | ||
return part.toBuffer(remainingSize); | ||
} |
@@ -25,7 +25,5 @@ // Copyright 2013 SAP AG. | ||
function Result(connection, options) { | ||
options = util.extend({ | ||
autoFetch: true | ||
}, options); | ||
options = options || {}; | ||
this._connection = connection; | ||
this._autoFetch = options.autoFetch; | ||
this._resultSetMode = options.resultSetMode || options.autoFetch === false; | ||
this._resultSetMetadata = undefined; | ||
@@ -44,3 +42,2 @@ this._parameterMetadata = undefined; | ||
Result.prototype.handle = function handle(err, reply, cb) { | ||
if (err) { | ||
@@ -50,35 +47,13 @@ return cb(err); | ||
var args = [null]; | ||
var resultSets = reply.resultSets || []; | ||
var outputParameters = {}; | ||
// handle missing resultSet metadata | ||
if (this._resultSetMetadata && resultSets.length) { | ||
if (!resultSets[0].metadata) { | ||
resultSets[0].metadata = this._resultSetMetadata; | ||
} | ||
} | ||
// output parameter | ||
if (this._parameterMetadata && util.isObject(reply.outputParameters)) { | ||
outputParameters = getOutputParameters(this._parameterMetadata, | ||
reply.outputParameters.buffer); | ||
} | ||
function onresults(err, results) { | ||
if (err) { | ||
return cb(err); | ||
} | ||
Array.prototype.push.apply(args, results); | ||
cb.apply(null, args); | ||
} | ||
switch (reply.functionCode) { | ||
case FunctionCode.SELECT: | ||
case FunctionCode.SELECT_FOR_UPDATE: | ||
this.createResultSets(resultSets, onresults); | ||
this.handleQuery(cb, this.createResultSets(reply.resultSets)); | ||
return; | ||
case FunctionCode.INSERT: | ||
case FunctionCode.UPDATE: | ||
this.handleModify(cb, reply.rowsAffected, reply.writeLobReply); | ||
return; | ||
case FunctionCode.DELETE: | ||
cb(null, reply.rowsAffected); | ||
this.handleDelete(cb, reply.rowsAffected); | ||
return; | ||
@@ -90,4 +65,5 @@ case FunctionCode.DDL: | ||
case FunctionCode.DB_PROCEDURE_CALL_WITH_RESULT: | ||
args.push(outputParameters); | ||
this.createResultSets(resultSets, onresults); | ||
this.handleDBCall(cb, | ||
this.createOutputParameters(reply.outputParameters), | ||
this.createResultSets(reply.resultSets)); | ||
return; | ||
@@ -97,39 +73,92 @@ default: | ||
cb(err); | ||
return; | ||
} | ||
}; | ||
Result.prototype.createResultSets = function createResultSets(resultSets, cb) { | ||
if (!resultSets.length) { | ||
return cb(null, []); | ||
Result.prototype.handleModify = function handleModify(cb, rowsAffected, | ||
writeLobReply) { | ||
cb(null, rowsAffected, writeLobReply); | ||
}; | ||
Result.prototype.handleDelete = function handleDelete(cb, rowsAffected) { | ||
cb(null, rowsAffected); | ||
}; | ||
Result.prototype.handleQuery = function handleQuery(cb, resultSets) { | ||
function done(err, results) { | ||
if (err) { | ||
return cb(err); | ||
} | ||
var args = [null]; | ||
Array.prototype.push.apply(args, results); | ||
return cb.apply(null, args); | ||
} | ||
var resultSetObjects = []; | ||
for (var i = 0; i < resultSets.length; i++) { | ||
resultSetObjects.push(new ResultSet(this._connection, resultSets[i])); | ||
if (this._resultSetMode) { | ||
return done(null, resultSets); | ||
} | ||
if (!this._autoFetch) { | ||
return cb(null, resultSetObjects); | ||
fetchAll(resultSets, done); | ||
}; | ||
Result.prototype.handleDBCall = function handleDBCall(cb, params, resultSets) { | ||
function done(err, results) { | ||
if (err) { | ||
return cb(err); | ||
} | ||
var args = [null, params || {}]; | ||
Array.prototype.push.apply(args, results); | ||
return cb.apply(null, args); | ||
} | ||
fetchAll(resultSetObjects, cb); | ||
if (this._resultSetMode) { | ||
return done(null, resultSets); | ||
} | ||
fetchAll(resultSets, done); | ||
}; | ||
function getOutputParameters(metadata, buffer) { | ||
var parser = new Parser(metadata, { | ||
nameProperty: 'name', | ||
}); | ||
return parser.parseParameters(buffer); | ||
function createResultSets(resultSets) { | ||
/* jshint validthis:true */ | ||
resultSets = resultSets || []; | ||
// handle missing resultSet metadata | ||
if (this._resultSetMetadata && resultSets.length) { | ||
if (!resultSets[0].metadata) { | ||
resultSets[0].metadata = this._resultSetMetadata; | ||
} | ||
} | ||
return resultSets.map(createResultSet, this._connection); | ||
} | ||
Result.prototype.createResultSets = createResultSets; | ||
function createOutputParameters(outputParameters) { | ||
/* jshint validthis:true */ | ||
if (this._parameterMetadata && util.isObject(outputParameters)) { | ||
return Parser.parseParameters(this._parameterMetadata, outputParameters.buffer); | ||
} | ||
return null; | ||
} | ||
Result.prototype.createOutputParameters = createOutputParameters; | ||
function createResultSet(rsd) { | ||
/* jshint validthis:true */ | ||
return new ResultSet(this, rsd); | ||
} | ||
function fetchAll(resultSets, cb) { | ||
var results = []; | ||
function isOpen(rs) { | ||
return !rs.closed; | ||
} | ||
function handleClose(err) { | ||
if (err) { | ||
// ignore errors | ||
} | ||
} | ||
function close(rs) { | ||
rs.close(handleClose); | ||
} | ||
function done(err) { | ||
resultSets.filter(function isOpen(rs) { | ||
return !rs.closed; | ||
}).forEach(function close(rs) { | ||
rs.close(function onclose(err) { | ||
console.log('close', err) | ||
}); | ||
}); | ||
resultSets.filter(isOpen).forEach(close); | ||
if (err) { | ||
@@ -145,3 +174,4 @@ return cb(err); | ||
} | ||
resultSets[i].fetch(function onfetch(err, rows) { | ||
function handleFetch(err, rows) { | ||
if (err) { | ||
@@ -152,5 +182,6 @@ return done(err); | ||
process.nextTick(next.bind(null, i + 1)); | ||
}); | ||
} | ||
resultSets[i].fetch(handleFetch); | ||
} | ||
next(0); | ||
} |
@@ -18,7 +18,8 @@ // Copyright 2013 SAP AG. | ||
var EventEmitter = require('events').EventEmitter; | ||
var Readable = require('stream').Readable; | ||
var common = require('./common'); | ||
var Parser = require('./Parser'); | ||
var Lob = require('./Lob'); | ||
var ResultSetAttributes = common.ResultSetAttributes; | ||
var TypeCode = common.TypeCode; | ||
var debug = util.debuglog('hdb_rs'); | ||
@@ -31,2 +32,4 @@ module.exports = ResultSet; | ||
ResultSet.DEFAULT_FETCH_SIZE = Math.pow(2, 10); | ||
ResultSet.DEFAULT_ROW_LENGTH = Math.pow(2, 6); | ||
ResultSet.DEFAULT_ARRAY_LENGTH = Math.pow(2, 8); | ||
@@ -46,2 +49,4 @@ function ResultSet(connection, rsd) { | ||
this._fetchSize = ResultSet.DEFAULT_FETCH_SIZE; | ||
this._rowLength = ResultSet.DEFAULT_ROW_LENGTH; | ||
this._readSize = Lob.DEFAULT_READ_SIZE; | ||
} | ||
@@ -58,2 +63,12 @@ | ||
} | ||
}, | ||
averageRowLength: { | ||
get: function getAverageRowLength() { | ||
return this._rowLength; | ||
} | ||
}, | ||
readSize: { | ||
get: function getReadSize() { | ||
return this._readSize; | ||
} | ||
} | ||
@@ -70,44 +85,30 @@ }); | ||
ResultSet.prototype.fetch = function fetch(cb) { | ||
var self = this; | ||
function done(err, rows) { | ||
/* jshint validthis:true */ | ||
if (util.isFunction(cb)) { | ||
cb(err, rows, self.closed); | ||
} | ||
} | ||
if (typeof this._running !== 'undefined') { | ||
var err = new Error('ResultSet invalid state error'); | ||
return done(err); | ||
} | ||
fetchRows(this, done); | ||
ResultSet.prototype.setAverageRowLength = function setAverageRowLength(length) { | ||
this._rowLength = length; | ||
return this; | ||
}; | ||
ResultSet.prototype.createReadStream = function createReadStream(options) { | ||
if (typeof this._running !== 'undefined') { | ||
return null; | ||
ResultSet.prototype.setReadSize = function setReadSize(readSize) { | ||
if (readSize > Lob.MAX_READ_SIZE) { | ||
this._readSize = Lob.MAX_READ_SIZE; | ||
} | ||
return createResultSetStream(this, options); | ||
this._readSize = readSize; | ||
return this; | ||
}; | ||
ResultSet.prototype.stopFetch = function stopFetch() { | ||
ResultSet.prototype.pause = function pause() { | ||
this._running = false; | ||
return this; | ||
}; | ||
ResultSet.prototype.startFetch = function startFetch() { | ||
if (!this._running && !this.finished) { | ||
this._running = true; | ||
if (util.isObject(this._data)) { | ||
handleData.call(this, this._data); | ||
this._data = undefined; | ||
} else { | ||
sendFetch.call(this); | ||
} | ||
ResultSet.prototype.resume = function resume() { | ||
if (this._running || this.finished) { | ||
return; | ||
} | ||
return this; | ||
this._running = true; | ||
if (util.isObject(this._data)) { | ||
handleData.call(this, this._data); | ||
this._data = undefined; | ||
} else { | ||
sendFetch.call(this); | ||
} | ||
}; | ||
@@ -121,193 +122,107 @@ | ||
} | ||
return this; | ||
}; | ||
ResultSet.prototype.createParser = function createParser(treshhold) { | ||
return new Parser(this.metadata, { | ||
treshhold: treshhold || 0 | ||
}); | ||
ResultSet.prototype.getLobColumnNames = function getLobColumnNames() { | ||
return this.metadata.filter(isLob).map(getColumName); | ||
}; | ||
ResultSet.prototype.readLob = function readLob(ld, cb) { | ||
Lob.create(this._connection, ld).read(cb); | ||
}; | ||
ResultSet.prototype.fetch = function fetch(cb) { | ||
var stream = this.createArrayStream(); | ||
var collector = new util.stream.Writable({ | ||
objectMode: true, | ||
highWaterMark: 16 | ||
}); | ||
collector.rows = []; | ||
collector.columns = this.getLobColumnNames(); | ||
if (collector.columns.length) { | ||
collector._write = collectLobRows; | ||
} else { | ||
collector._write = collectRows; | ||
} | ||
ResultSet.prototype.createLobStream = function createLobStream(ld, options) { | ||
return new Lob(this._connection, ld).createReadStream(options); | ||
}; | ||
function createResultSetStream(rs, options) { | ||
var parser = rs.createParser(); | ||
options = options || {}; | ||
options.objectMode = true; | ||
var readable = new Readable(options); | ||
readable._read = function _read() { | ||
rs.startFetch(); | ||
}; | ||
var finished = false; | ||
var pending = 0; | ||
function cleanup() { | ||
if (finished) { | ||
return; | ||
} | ||
finished = true; | ||
rs.removeListener('error', onerror); | ||
rs.removeListener('data', ondata); | ||
rs.removeListener('end', onend); | ||
function done(err, rows) { | ||
stream.removeListener('error', onerror); | ||
collector.removeListener('finish', onfinish); | ||
cb(err, rows); | ||
} | ||
function onerror(err) { | ||
cleanup(); | ||
readable.emit('error', err); | ||
done(err); | ||
} | ||
rs.once('error', onerror); | ||
function ondata(count, chunk) { | ||
var rows = []; | ||
pending += 1; | ||
parser.parse(chunk, rows, function parsed(err) { | ||
pending -= 1; | ||
if (err) { | ||
cleanup(); | ||
readable.emit('error', err); | ||
return; | ||
} | ||
if (!readable.push(rows)) { | ||
rs.stopFetch(); | ||
} | ||
if (pending === 0 && finished) { | ||
readable.push(null); | ||
} | ||
}); | ||
function onfinish() { | ||
done(null, this.rows); | ||
} | ||
rs.on('data', ondata); | ||
function onend(closed) { | ||
cleanup(); | ||
if (!closed && rs._close) { | ||
sendClose.call(rs); | ||
} | ||
if (pending === 0) { | ||
readable.push(null); | ||
} | ||
} | ||
rs.once('end', onend); | ||
return readable; | ||
stream.on('error', onerror).pipe(collector).on('finish', onfinish); | ||
} | ||
function rowReadLob(connection, row, name, cb) { | ||
if (!row[name] || Buffer.isBuffer(row[name])) { | ||
return cb(); | ||
ResultSet.prototype.createBinaryStream = function createBinaryStream(options) { | ||
if (!util.isUndefined(this._running)) { | ||
return null; | ||
} | ||
function done(err, buffer) { | ||
if (err) { | ||
return cb(err); | ||
} | ||
row[name] = buffer; | ||
cb(); | ||
} | ||
Lob.create(connection, row[name]).read(done); | ||
} | ||
options = util.extend({ | ||
highWaterMark: Math.floor(1.5 * this._fetchSize * this._rowLength) | ||
}, options); | ||
options.objectMode = false; | ||
return util.createReadStream(this, ['error', 'close'], options); | ||
}; | ||
function rowReadLobs(connection, row, names, cb) { | ||
var i = 0; | ||
ResultSet.prototype.createParser = function createParser(options) { | ||
return Parser.create(this.metadata).createTransform(this, options); | ||
}; | ||
function next() { | ||
// ignore errors | ||
if (i === names.length) { | ||
return cb(); | ||
} | ||
process.nextTick(rowReadLob.bind(null, connection, row, names[i], next)); | ||
i += 1; | ||
} | ||
next(); | ||
} | ||
ResultSet.prototype.createObjectStream = function createObjectStream(options) { | ||
options = util.extend({}, options); | ||
options.arrayMode = false; | ||
function readLobs(connection, rows, names, cb) { | ||
var i = 0; | ||
return util.pipe(this.createBinaryStream(), this.createParser(options)); | ||
}; | ||
function next() { | ||
// ignore errors | ||
if (i === rows.length) { | ||
return cb(null, rows); | ||
} | ||
process.nextTick(rowReadLobs.bind(null, connection, rows[i], names, next)); | ||
i += 1; | ||
ResultSet.prototype.createArrayStream = function createArrayStream(options) { | ||
if (util.isNumber(options) || options === true) { | ||
options = { | ||
arrayMode: options | ||
}; | ||
} else if (util.isObject(options)) { | ||
options = util.extend({}, options); | ||
} else { | ||
options = { | ||
arrayMode: ResultSet.DEFAULT_ARRAY_LENGTH | ||
}; | ||
} | ||
next(); | ||
} | ||
function fetchRows(rs, cb) { | ||
return util.pipe(this.createBinaryStream(), this.createParser(options)); | ||
}; | ||
var parser = rs.createParser(); | ||
var lobs = rs.metadata.filter(isLob).map(getColumName); | ||
var rows = []; | ||
var finished = false; | ||
var pending = 0; | ||
ResultSet.prototype.createReadStream = function createReadStream(options) { | ||
options = options || {}; | ||
function done(err) { | ||
if (err) { | ||
return cb(err); | ||
} | ||
if (!lobs.length) { | ||
return cb(null, rows); | ||
} | ||
readLobs(rs._connection, rows, lobs, cb); | ||
if (options.objectMode === false) { | ||
return this.createBinaryStream(options); | ||
} | ||
function cleanup() { | ||
if (finished) { | ||
return; | ||
} | ||
finished = true; | ||
rs.removeListener('error', onerror); | ||
rs.removeListener('data', ondata); | ||
rs.removeListener('end', onend); | ||
if (options.arrayMode) { | ||
return this.createArrayStream(options); | ||
} | ||
return this.createObjectStream(options); | ||
}; | ||
function onerror(err) { | ||
cleanup(); | ||
done(err); | ||
} | ||
rs.on('error', onerror); | ||
ResultSet.prototype.createLob = function createLob(ld, options) { | ||
options = util.extend({ | ||
readSize: this._readSize | ||
}, options); | ||
return new Lob(sendReadLob.bind(this), ld, options); | ||
}; | ||
function ondata(count, chunk) { | ||
pending += 1; | ||
parser.parse(chunk, rows, function parsed(err) { | ||
pending -= 1; | ||
if (err) { | ||
cleanup(); | ||
done(err); | ||
return; | ||
} | ||
if (pending === 0 && finished) { | ||
done(null); | ||
} | ||
}); | ||
} | ||
rs.on('data', ondata); | ||
function onend() { | ||
cleanup(); | ||
if (pending === 0) { | ||
done(null); | ||
} | ||
} | ||
rs.on('end', onend); | ||
rs.startFetch(); | ||
} | ||
function sendClose() { | ||
/* jshint validthis:true */ | ||
debug('sendClose'); | ||
function done(err) { | ||
this._connection = undefined; | ||
if (!err) { | ||
if (err) { | ||
debug('close failed: %s', err); | ||
} else { | ||
this.closed = true; | ||
emitClose.call(this); | ||
this.emit('close'); | ||
@@ -326,3 +241,3 @@ } | ||
/* jshint validthis:true */ | ||
debug('sendFetch(%d)', this.fetchSize); | ||
this._connection.fetchNext({ | ||
@@ -334,7 +249,13 @@ resultSetId: this.id, | ||
function sendReadLob(req, cb) { | ||
/* jshint validthis:true */ | ||
this._connection.readLob(req, cb); | ||
} | ||
function receiveData(err, reply) { | ||
/* jshint validthis:true */ | ||
debug('receiveData()'); | ||
if (err) { | ||
this._running = false; | ||
debug('emit "error": %s', err); | ||
this.emit('error', err); | ||
@@ -354,5 +275,6 @@ return; | ||
/* jshint validthis:true */ | ||
debug('handleData(%d)', data.argumentCount); | ||
if (data.argumentCount && Buffer.isBuffer(data.buffer)) { | ||
this.emit('data', data.argumentCount, data.buffer); | ||
debug('emit "data": length=%d', data.buffer.length); | ||
this.emit('data', data.buffer); | ||
} | ||
@@ -364,8 +286,3 @@ | ||
this.closed = isClosed(data); | ||
this.emit('end', this.closed); | ||
if (this.closed) { | ||
process.nextTick(this.emit.bind(this, 'close')); | ||
} else if (util.isFunction(this._close)) { | ||
sendClose.call(this); | ||
} | ||
process.nextTick(emitEnd.bind(this)); | ||
return; | ||
@@ -379,5 +296,22 @@ } | ||
function emitEnd() { | ||
/* jshint validthis:true */ | ||
debug('emit "end"'); | ||
this.emit('end'); | ||
if (this.closed) { | ||
process.nextTick(emitClose.bind(this)); | ||
} else if (util.isFunction(this._close)) { | ||
sendClose.call(this); | ||
} | ||
} | ||
function emitClose() { | ||
/* jshint validthis:true */ | ||
debug('emit "close"'); | ||
this.emit('close'); | ||
} | ||
function isLast(data) { | ||
/* jshint bitwise:false */ | ||
return !!(data.attributes & common.ResultSetAttributes.LAST); | ||
return !!(data.attributes & ResultSetAttributes.LAST); | ||
} | ||
@@ -387,3 +321,3 @@ | ||
/* jshint bitwise:false */ | ||
return !!(data.attributes & common.ResultSetAttributes.CLOSED); | ||
return !!(data.attributes & ResultSetAttributes.CLOSED); | ||
} | ||
@@ -407,2 +341,63 @@ | ||
return column.columnDisplayName; | ||
} | ||
function collectRows(rows, encoding, done) { | ||
/* jshint validthis:true */ | ||
for (var i = 0; i < rows.length; i++) { | ||
this.rows.push(rows[i]); | ||
} | ||
done(); | ||
} | ||
function collectLobRows(rows, encoding, done) { | ||
/* jshint validthis:true */ | ||
var self = this; | ||
var i = 0; | ||
function handleRow(err) { | ||
if (err) { | ||
return done(err); | ||
} | ||
// next row | ||
i += 1; | ||
next(); | ||
} | ||
function next() { | ||
if (i === rows.length) { | ||
return done(null, rows); | ||
} | ||
util.setImmediate(collectLobRow.bind(self, rows[i], handleRow)); | ||
} | ||
next(); | ||
} | ||
function collectLobRow(row, done) { | ||
/* jshint validthis:true */ | ||
var self = this; | ||
var i = 0; | ||
function receiveLob(err, buffer) { | ||
if (err) { | ||
return done(err); | ||
} | ||
// update lob | ||
row[self.columns[i]] = buffer; | ||
// next lob | ||
i += 1; | ||
next(); | ||
} | ||
function next() { | ||
if (i === self.columns.length) { | ||
self.rows.push(row); | ||
return done(null); | ||
} | ||
var lob = row[self.columns[i]]; | ||
if (lob === null || Buffer.isBuffer(lob)) { | ||
return receiveLob(null, lob); | ||
} | ||
lob.read(receiveLob); | ||
} | ||
next(); | ||
} |
@@ -37,22 +37,11 @@ // Copyright 2013 SAP AG. | ||
}; | ||
if (util.isFunction(options)) { | ||
cb = options; | ||
options = defaults; | ||
} else if (util.isObject(options)) { | ||
options = util.extend(defaults, options); | ||
} else { | ||
var autoFetch = !! options; | ||
options = defaults; | ||
options.autoFetch = autoFetch; | ||
} | ||
executeStatement.call(this, defaults, values, options, cb); | ||
return this; | ||
}; | ||
var result = new Result(this._connection, options); | ||
result.setResultSetMetadata(this.resultSetMetadata); | ||
result.setParameterMetadata(this.parameterMetadata.filter(isOutputParameter)); | ||
this._connection.execute({ | ||
statementId: this.id, | ||
parameters: getInputParameters(this.parameterMetadata, values) | ||
}, function onreply(err, reply) { | ||
result.handle(err, reply, cb); | ||
}); | ||
Statement.prototype.execute = function execute(values, options, cb) { | ||
var defaults = { | ||
autoFetch: false | ||
}; | ||
executeStatement.call(this, defaults, values, options, cb); | ||
return this; | ||
@@ -120,2 +109,26 @@ }; | ||
function executeStatement(defaults, values, options, cb) { | ||
/* jshint validthis:true */ | ||
if (util.isFunction(options)) { | ||
cb = options; | ||
options = defaults; | ||
} else if (util.isObject(options)) { | ||
options = util.extend(defaults, options); | ||
} else { | ||
var autoFetch = !! options; | ||
options = defaults; | ||
options.autoFetch = autoFetch; | ||
} | ||
var result = new Result(this._connection, options); | ||
result.setResultSetMetadata(this.resultSetMetadata); | ||
result.setParameterMetadata(this.parameterMetadata.filter(isOutputParameter)); | ||
this._connection.execute({ | ||
statementId: this.id, | ||
parameters: getInputParameters(this.parameterMetadata, values) | ||
}, function onreply(err, reply) { | ||
result.handle(err, reply, cb); | ||
}); | ||
} | ||
function isInputParameter(metadata) { | ||
@@ -122,0 +135,0 @@ return metadata.ioType === IoType.INPUT || metadata.ioType === IoType.IN_OUT; |
@@ -16,4 +16,4 @@ // Copyright 2013 SAP AG. | ||
var util = require('util'); | ||
var Transform = require('stream').Transform; | ||
var util = require('../util'); | ||
var Transform = util.stream.Transform; | ||
@@ -30,4 +30,2 @@ module.exports = Stringifier; | ||
this._writableState.objectMode = true; | ||
this._map = options.map; | ||
this._header = options.header !== undefined ? options.header : '['; | ||
@@ -37,23 +35,21 @@ this._footer = options.footer !== undefined ? options.footer : ']'; | ||
this._stringify = options.stringify || JSON.stringify; | ||
this._map = undefined; | ||
if (util.isFunction(options.map)) { | ||
this._map = options.map; | ||
} | ||
this._first = true; | ||
} | ||
Stringifier.prototype._transform = function (chunk, encoding, done) { | ||
var str = ''; | ||
if (chunk && chunk.length) { | ||
if (typeof this._map === 'function') { | ||
chunk = chunk.map(this.map); | ||
} | ||
str = this._first ? this._header : this._seperator; | ||
this._first = false; | ||
for (var i = 0; i < chunk.length; i++) { | ||
if (i > 0) | ||
str += this._seperator; | ||
str += this._stringify(chunk[i]); | ||
} | ||
Stringifier.prototype._transform = function _transform(thing, encoding, done) { | ||
if (util.isArray(thing) && thing.length) { | ||
this.push(this.transformRows(thing)); | ||
} else if (util.isObject(thing)) { | ||
this.push(this.transformRow(thing)); | ||
} else { | ||
this.push(''); | ||
} | ||
done(null, str); | ||
done(); | ||
}; | ||
Stringifier.prototype._flush = function (done) { | ||
Stringifier.prototype._flush = function _flush(done) { | ||
if (this._first) { | ||
@@ -65,2 +61,28 @@ this.push(this._header + this._footer); | ||
done(null); | ||
}; | ||
Stringifier.prototype.transformRows = function transformRows(rows) { | ||
if (this._map) { | ||
rows = rows.map(this._map); | ||
} | ||
var str = this._first ? this._header : this._seperator; | ||
this._first = false; | ||
for (var i = 0; i < rows.length; i++) { | ||
if (i > 0) { | ||
str += this._seperator; | ||
} | ||
str += this._stringify(rows[i]); | ||
} | ||
return str; | ||
}; | ||
Stringifier.prototype.transformRow = function transformRow(row) { | ||
if (this._map) { | ||
row = this._map(row); | ||
} | ||
if (this._first) { | ||
this._first = false; | ||
return this._header + this._stringify(row); | ||
} | ||
return this._seperator + this._stringify(row); | ||
}; |
@@ -31,3 +31,2 @@ // Copyright 2013 SAP AG. | ||
var INT_10_13 = Math.pow(10, 13); | ||
var INT_10_14 = Math.pow(10, 14); | ||
@@ -71,12 +70,37 @@ var ZERO_1 = '0'; | ||
/* 10^7 for base 2^16 */ | ||
var BIN_10_7_0 = INT_10_7 % INT_2_16; | ||
var BIN_10_7_1 = Math.floor(INT_10_7 / INT_2_16); | ||
var BIN_10_7_0 = 38528; | ||
var BIN_10_7_1 = 152; | ||
/* 10^14 for base 2^16 */ | ||
var BIN_10_14_0 = INT_10_14 % INT_2_16; | ||
var BIN_10_14_1 = Math.floor(INT_10_14 / INT_2_16) % INT_2_16; | ||
var BIN_10_14_2 = Math.floor(Math.floor(INT_10_14 / INT_2_16) / INT_2_16); | ||
var BIN_10_14_0 = 16384; | ||
var BIN_10_14_1 = 4218; | ||
var BIN_10_14_2 = 23283; | ||
/* 10^21 for base 2^16 */ | ||
var BIN_10_21_0 = 0; | ||
var BIN_10_21_1 = 56992; | ||
var BIN_10_21_2 = 44485; | ||
var BIN_10_21_3 = 13769; | ||
var BIN_10_21_4 = 54; | ||
/* 10^28 for base 2^16 */ | ||
var BIN_10_28_0 = 0; | ||
var BIN_10_28_1 = 4096; | ||
var BIN_10_28_2 = 609; | ||
var BIN_10_28_3 = 15909; | ||
var BIN_10_28_4 = 52830; | ||
var BIN_10_28_5 = 8271; | ||
/* 10^35 for base 2^16 */ | ||
var BIN_10_35_0 = 0; | ||
var BIN_10_35_1 = 0; | ||
var BIN_10_35_2 = 36840; | ||
var BIN_10_35_3 = 11143; | ||
var BIN_10_35_4 = 19842; | ||
var BIN_10_35_5 = 29383; | ||
var BIN_10_35_6 = 16993; | ||
var BIN_10_35_7 = 19; | ||
/* Decimal zero padding */ | ||
var MAX_DECIMAL_LENGTH = 34; | ||
var MAX_DECIMAL_LENGTH = 35; | ||
var ZEROS = ['']; | ||
@@ -200,2 +224,39 @@ for (var i = 1; i < MAX_DECIMAL_LENGTH; i++) { | ||
function isUInt64(value) { | ||
if (typeof value === 'number') { | ||
return true; | ||
} | ||
if (value.length < 20) { | ||
return true; | ||
} | ||
if (value.length === 20 && value <= '18446744073709551616') { | ||
return true; | ||
} | ||
return false; | ||
} | ||
function writeDec128(buffer, value, offset) { | ||
offset = offset || 0; | ||
buffer.fill(0x00, offset, offset + 16); | ||
if (isUInt64(value.m)) { | ||
writeUInt64(buffer, value.m, offset); | ||
} else { | ||
writeUInt128(buffer, value.m, offset); | ||
} | ||
var e = EXP_BIAS + value.e; | ||
var e0 = e << 1; | ||
e0 &= 0xfe; | ||
e0 |= buffer[offset + 14] & 0x01; | ||
buffer[offset + 14] = e0; | ||
var e1 = e >> 7; | ||
if (value.s < 0) { | ||
e1 |= 0x80; | ||
} | ||
buffer[offset + 15] = e1; | ||
} | ||
function readDec128(buffer, offset) { | ||
@@ -237,3 +298,4 @@ | ||
m: undefined, | ||
e: ((((buffer[offset + 1] << 8) | buffer[offset]) & 0x7ffe) >> 1) - EXP_BIAS | ||
e: ((((buffer[offset + 1] << 8) | buffer[offset]) & 0x7ffe) >> 1) - | ||
EXP_BIAS | ||
}; | ||
@@ -348,2 +410,5 @@ | ||
var value = readDec128(buffer, offset); | ||
if (value === null) { | ||
return null; | ||
} | ||
if (value.s === -1) { | ||
@@ -357,2 +422,5 @@ return '-' + value.m + 'e' + value.e; | ||
var value = readDec128(buffer, offset); | ||
if (value === null) { | ||
return null; | ||
} | ||
var d = value.m; | ||
@@ -401,3 +469,3 @@ if (typeof d === 'number') { | ||
a = +value.substring(l - 7); | ||
b = +value.substring(l - 7, l - 14); | ||
b = +value.substring(l - 14, l - 7); | ||
c = +value.substring(0, l - 14); | ||
@@ -497,2 +565,303 @@ | ||
function readUInt128(buffer, offset) { | ||
var i, j, k, l, z0, z1, y0, y1, y2, x0, x1, x2, x3, x4; | ||
offset = offset || 0; | ||
i = buffer[offset + 2] << 16; | ||
i |= buffer[offset + 1] << 8; | ||
i |= buffer[offset]; | ||
i += buffer[offset + 3] << 24 >>> 0; | ||
offset += 4; | ||
j = buffer[offset + 2] << 16; | ||
j |= buffer[offset + 1] << 8; | ||
j |= buffer[offset]; | ||
j += buffer[offset + 3] << 24 >>> 0; | ||
offset += 4; | ||
k = buffer[offset + 2] << 16; | ||
k |= buffer[offset + 1] << 8; | ||
k |= buffer[offset]; | ||
k += buffer[offset + 3] << 24 >>> 0; | ||
offset += 4; | ||
l = (buffer[offset + 2]) << 16; | ||
l |= buffer[offset + 1] << 8; | ||
l |= buffer[offset]; | ||
l += buffer[offset + 3] << 24 >>> 0; | ||
if (k === 0 && l === 0) { | ||
if (j === 0) { | ||
return i; | ||
} | ||
if (j < INT_2_21 || (j === INT_2_21 && i === 0)) { | ||
return j * INT_2_32 + i; | ||
} | ||
} | ||
if (i < BASE) { | ||
x0 = i; | ||
x1 = 0; | ||
} else { | ||
x0 = i % BASE; | ||
x1 = Math.floor(i / BASE); | ||
} | ||
if (j < BASE) { | ||
x0 += j * INT_2_32_0; | ||
x1 += j * INT_2_32_1; | ||
x2 = 0; | ||
} else { | ||
z0 = j % BASE; | ||
z1 = Math.floor(j / BASE); | ||
x0 += z0 * INT_2_32_0; | ||
x1 += z0 * INT_2_32_1 + z1 * INT_2_32_0; | ||
x2 = z1 * INT_2_32_1; | ||
} | ||
if (k < BASE) { | ||
y0 = k; | ||
y1 = 0; | ||
} else { | ||
y0 = k % BASE; | ||
y1 = Math.floor(k / BASE); | ||
} | ||
if (l < BASE) { | ||
y0 += l * INT_2_32_0; | ||
y1 += l * INT_2_32_1; | ||
y2 = 0; | ||
} else { | ||
z0 = l % BASE; | ||
z1 = Math.floor(l / BASE); | ||
y0 += z0 * INT_2_32_0; | ||
y1 += z0 * INT_2_32_1 + z1 * INT_2_32_0; | ||
y2 = z1 * INT_2_32_1; | ||
} | ||
if (y0 >= BASE) { | ||
y1 += Math.floor(y0 / BASE); | ||
y0 %= BASE; | ||
} | ||
if (y1 >= BASE) { | ||
y2 += Math.floor(y1 / BASE); | ||
y1 %= BASE; | ||
} | ||
x0 += y0 * INT_2_64_0; | ||
x1 += y0 * INT_2_64_1 + y1 * INT_2_64_0; | ||
x2 += y0 * INT_2_64_2 + y1 * INT_2_64_1 + y2 * INT_2_64_0; | ||
x3 = y1 * INT_2_64_2 + y2 * INT_2_64_1; | ||
x4 = y2 * INT_2_64_2; | ||
if (x0 >= BASE) { | ||
x1 += Math.floor(x0 / BASE); | ||
x0 %= BASE; | ||
} | ||
if (x1 >= BASE) { | ||
x2 += Math.floor(x1 / BASE); | ||
x1 %= BASE; | ||
} | ||
if (x2 >= BASE) { | ||
x3 += Math.floor(x2 / BASE); | ||
x2 %= BASE; | ||
} | ||
if (x3 >= BASE) { | ||
x4 += Math.floor(x3 / BASE); | ||
x3 %= BASE; | ||
} | ||
if (x4) { | ||
return '' + x4 + lpad14(x3 * BASE + x2) + lpad14(x1 * BASE + x0); | ||
} | ||
if (x3) { | ||
return '' + (x3 * BASE + x2) + lpad14(x1 * BASE + x0); | ||
} | ||
if (x2) { | ||
if (x2 < INT_2_53_2 || (x2 === INT_2_53_2 && | ||
(x1 < INT_2_53_1 || (x1 === INT_2_53_1 && x0 <= INT_2_53_0)))) { | ||
return (x2 * BASE + x1) * BASE + x0; | ||
} | ||
return '' + x2 + lpad14(x1 * BASE + x0); | ||
} | ||
return x1 * BASE + x0; | ||
} | ||
function writeUInt128(buffer, value, offset) { | ||
var l, a, b, c, d, e, f, | ||
x0, x1, x2, x3, y0, y1, z0, z1, z2, z3, z4, z5, z6, z7; | ||
l = value.length; | ||
a = ~~value.substring(l - 7); | ||
if (l > 7) { | ||
b = ~~value.substring(l - 14, l - 7); | ||
} else { | ||
b = 0; | ||
} | ||
if (l > 14) { | ||
c = ~~value.substring(l - 21, l - 14); | ||
} else { | ||
c = 0; | ||
} | ||
if (l > 21) { | ||
d = ~~value.substring(l - 28, l - 21); | ||
} else { | ||
d = 0; | ||
} | ||
if (l > 28) { | ||
e = ~~value.substring(l - 35, l - 28); | ||
} else { | ||
e = 0; | ||
} | ||
if (l > 35) { | ||
f = ~~value.substring(0, l - 35); | ||
} else { | ||
f = 0; | ||
} | ||
z0 = z1 = z2 = z3 = z4 = z5 = z6 = z7 = 0; | ||
// set a * 10^0 | ||
if (a) { | ||
z0 = a % INT_2_16; | ||
z1 = Math.floor(a / INT_2_16); | ||
} | ||
// add b * 10^7 | ||
if (b) { | ||
y0 = b % INT_2_16; | ||
y1 = Math.floor(b / INT_2_16); | ||
if (y0 >= INT_2_16) { | ||
y1 += Math.floor(y0 / INT_2_16); | ||
y0 %= INT_2_16; | ||
} | ||
z0 += y0 * BIN_10_7_0; | ||
z1 += y0 * BIN_10_7_1 + y1 * BIN_10_7_0; | ||
z2 += y1 * BIN_10_7_1; | ||
} | ||
// add c * 10^14 | ||
if (c) { | ||
y0 = c % INT_2_16; | ||
y1 = Math.floor(c / INT_2_16); | ||
if (y0 >= INT_2_16) { | ||
y1 += Math.floor(y0 / INT_2_16); | ||
y0 %= INT_2_16; | ||
} | ||
z0 += y0 * BIN_10_14_0; | ||
z1 += y0 * BIN_10_14_1 + y1 * BIN_10_14_0; | ||
z2 += y0 * BIN_10_14_2 + y1 * BIN_10_14_1; | ||
z3 += y1 * BIN_10_14_2; | ||
} | ||
// add d * 10^21 | ||
if (d) { | ||
y0 = d % INT_2_16; | ||
y1 = Math.floor(d / INT_2_16); | ||
if (y0 >= INT_2_16) { | ||
y1 += Math.floor(y0 / INT_2_16); | ||
y0 %= INT_2_16; | ||
} | ||
z0 += y0 * BIN_10_21_0; | ||
z1 += y0 * BIN_10_21_1 + y1 * BIN_10_21_0; | ||
z2 += y0 * BIN_10_21_2 + y1 * BIN_10_21_1; | ||
z3 += y0 * BIN_10_21_3 + y1 * BIN_10_21_2; | ||
z4 += y0 * BIN_10_21_4 + y1 * BIN_10_21_3; | ||
z5 += y1 * BIN_10_21_4; | ||
} | ||
// add e * 10^28 | ||
if (e) { | ||
y0 = e % INT_2_16; | ||
y1 = Math.floor(e / INT_2_16); | ||
if (y0 >= INT_2_16) { | ||
y1 += Math.floor(y0 / INT_2_16); | ||
y0 %= INT_2_16; | ||
} | ||
z0 += y0 * BIN_10_28_0; | ||
z1 += y0 * BIN_10_28_1 + y1 * BIN_10_28_0; | ||
z2 += y0 * BIN_10_28_2 + y1 * BIN_10_28_1; | ||
z3 += y0 * BIN_10_28_3 + y1 * BIN_10_28_2; | ||
z4 += y0 * BIN_10_28_4 + y1 * BIN_10_28_3; | ||
z5 += y0 * BIN_10_28_5 + y1 * BIN_10_28_4; | ||
z6 += y1 * BIN_10_28_5; | ||
} | ||
// add f * 10^35 | ||
if (f) { | ||
y0 = f % INT_2_16; | ||
z0 += y0 * BIN_10_35_0; | ||
z1 += y0 * BIN_10_35_1; | ||
z2 += y0 * BIN_10_35_2; | ||
z3 += y0 * BIN_10_35_3; | ||
z4 += y0 * BIN_10_35_4; | ||
z5 += y0 * BIN_10_35_5; | ||
z6 += y0 * BIN_10_35_6; | ||
z7 += y0 * BIN_10_35_7; | ||
} | ||
if (z0 >= INT_2_16) { | ||
z1 += Math.floor(z0 / INT_2_16); | ||
z0 %= INT_2_16; | ||
} | ||
if (z1 >= INT_2_16) { | ||
z2 += Math.floor(z1 / INT_2_16); | ||
z1 %= INT_2_16; | ||
} | ||
if (z2 >= INT_2_16) { | ||
z3 += Math.floor(z2 / INT_2_16); | ||
z2 %= INT_2_16; | ||
} | ||
if (z3 >= INT_2_16) { | ||
z4 += Math.floor(z3 / INT_2_16); | ||
z3 %= INT_2_16; | ||
} | ||
if (z4 >= INT_2_16) { | ||
z5 += Math.floor(z4 / INT_2_16); | ||
z4 %= INT_2_16; | ||
} | ||
if (z5 >= INT_2_16) { | ||
z6 += Math.floor(z5 / INT_2_16); | ||
z5 %= INT_2_16; | ||
} | ||
if (z6 >= INT_2_16) { | ||
z7 += Math.floor(z6 / INT_2_16); | ||
z6 %= INT_2_16; | ||
} | ||
x0 = z1 * INT_2_16 + z0; | ||
x1 = z3 * INT_2_16 + z2; | ||
x2 = z5 * INT_2_16 + z4; | ||
x3 = z7 * INT_2_16 + z6; | ||
buffer[offset + 3] = (x0 >>> 24) & 0xff; | ||
buffer[offset + 2] = (x0 >>> 16) & 0xff; | ||
buffer[offset + 1] = (x0 >>> 8) & 0xff; | ||
buffer[offset] = x0 & 0xff; | ||
offset += 4; | ||
buffer[offset + 3] = (x1 >>> 24) & 0xff; | ||
buffer[offset + 2] = (x1 >>> 16) & 0xff; | ||
buffer[offset + 1] = (x1 >>> 8) & 0xff; | ||
buffer[offset] = x1 & 0xff; | ||
offset += 4; | ||
buffer[offset + 3] = (x2 >>> 24) & 0xff; | ||
buffer[offset + 2] = (x2 >>> 16) & 0xff; | ||
buffer[offset + 1] = (x2 >>> 8) & 0xff; | ||
buffer[offset] = x2 & 0xff; | ||
offset += 4; | ||
buffer[offset + 3] = (x3 >>> 24) & 0xff; | ||
buffer[offset + 2] = (x3 >>> 16) & 0xff; | ||
buffer[offset + 1] = (x3 >>> 8) & 0xff; | ||
buffer[offset] = x3 & 0xff; | ||
} | ||
function readInt64(buffer, offset) { | ||
@@ -514,18 +883,2 @@ return _readInt64(buffer, offset || 0, false); | ||
Buffer.prototype.readInt64LE = function readInt64LE(offset) { | ||
return _readInt64(this, offset || 0, false); | ||
}; | ||
Buffer.prototype.readUInt64LE = function readUInt64LE(offset) { | ||
return _readInt64(this, offset || 0, true); | ||
}; | ||
Buffer.prototype.writeInt64LE = function writeInt64LE(value, offset) { | ||
_writeInt64(this, value, offset || 0, false); | ||
}; | ||
Buffer.prototype.writeUInt64LE = function writeUInt64LE(value, offset) { | ||
_writeInt64(this, value, offset || 0, true); | ||
}; | ||
exports.readInt64LE = readInt64; | ||
@@ -535,3 +888,6 @@ exports.readUInt64LE = readUInt64; | ||
exports.writeUInt64LE = writeUInt64; | ||
exports.readUInt128LE = readUInt128; | ||
exports.writeUInt128LE = writeUInt128; | ||
exports.readDec128 = readDec128; | ||
exports.writeDec128 = writeDec128; | ||
exports.readDecFloat = readDecFloat; | ||
@@ -538,0 +894,0 @@ exports.readDecFixed = readDecFixed; |
@@ -16,4 +16,28 @@ // Copyright 2013 SAP AG. | ||
/* jshint camelcase:false */ | ||
var util = require('util'); | ||
var stream = require('stream'); | ||
if (isFunction(setImmediate)) { | ||
exports.setImmediate = setImmediate; | ||
} else { | ||
// inoffical 0.8.x support | ||
exports.setImmediate = process.nextTick; | ||
} | ||
if (!stream.Readable) { | ||
// inoffical 0.8.x support | ||
stream = require('readable-stream'); | ||
} | ||
exports.stream = { | ||
Readable: stream.Readable, | ||
Writable: stream.Writable, | ||
Transform: stream.Transform | ||
}; | ||
function exportNativeUtil(fn) { | ||
exports[fn] = util[fn]; | ||
} | ||
[ | ||
@@ -32,6 +56,19 @@ 'format', | ||
'inherits' | ||
].forEach(function exportNativeUtils(fn) { | ||
exports[fn] = util[fn]; | ||
}); | ||
].forEach(exportNativeUtil); | ||
var debuglog; | ||
if (util.debuglog) { | ||
debuglog = util.debuglog; | ||
} else { | ||
try { | ||
debuglog = require('debuglog'); | ||
} catch (err) { | ||
/* jshint unused:false */ | ||
debuglog = function dbglg(name) { | ||
return function () {}; | ||
}; | ||
} | ||
} | ||
exports.debuglog = debuglog; | ||
exports.bignum = require('./bignum'); | ||
@@ -42,3 +79,3 @@ | ||
function extend(obj) { | ||
Array.prototype.slice.call(arguments, 1).forEach(function extendOnce(source) { | ||
function extendOnce(source) { | ||
/* jshint forin:false */ | ||
@@ -50,3 +87,4 @@ if (source) { | ||
} | ||
}); | ||
} | ||
Array.prototype.slice.call(arguments, 1).forEach(extendOnce); | ||
return obj; | ||
@@ -61,7 +99,2 @@ } | ||
function isNull(arg) { | ||
return arg === null; | ||
} | ||
exports.isNull = isNull; | ||
function isNumber(arg) { | ||
@@ -92,7 +125,2 @@ return typeof arg === 'number'; | ||
function isBuffer(arg) { | ||
return arg instanceof Buffer; | ||
} | ||
exports.isBuffer = isBuffer; | ||
function alignLength(length, alignment) { | ||
@@ -116,2 +144,85 @@ if (length % alignment === 0) { | ||
} | ||
exports._2cc = _2cc; | ||
exports._2cc = _2cc; | ||
function readData(ds, cb) { | ||
var length = 0; | ||
var chunks = []; | ||
function done(err) { | ||
ds.removeListener('error', onerror); | ||
ds.removeListener('data', ondata); | ||
ds.removeListener('end', onend); | ||
if (isFunction(cb)) { | ||
if (err) { | ||
return cb(err); | ||
} | ||
cb(null, Buffer.concat(chunks, length)); | ||
} | ||
} | ||
function onerror(err) { | ||
done(err); | ||
} | ||
ds.on('error', onerror); | ||
function ondata(chunk) { | ||
chunks.push(chunk); | ||
length += chunk.length; | ||
} | ||
ds.on('data', ondata); | ||
function onend() { | ||
done(null); | ||
} | ||
ds.on('end', onend); | ||
ds.resume(); | ||
} | ||
exports.readData = readData; | ||
function proxyEvents(source, target, events) { | ||
function proxyEvent(ev) { | ||
source.on(ev, target.emit.bind(target, ev)); | ||
} | ||
events.forEach(proxyEvent); | ||
return target; | ||
} | ||
exports.proxyEvents = proxyEvents; | ||
function createReadStream(ds, events, options) { | ||
if (!util.isArray(events)) { | ||
options = events; | ||
events = ['error', 'close']; | ||
} | ||
var readable = new stream.Readable(options); | ||
readable._read = function _read() { | ||
ds.resume(); | ||
}; | ||
function onend() { | ||
readable.push(null); | ||
} | ||
ds.once('end', onend); | ||
function ondata(chunk) { | ||
if (!chunk || !chunk.length) { | ||
return; | ||
} | ||
if (!readable.push(chunk)) { | ||
ds.pause(); | ||
} | ||
} | ||
ds.on('data', ondata); | ||
proxyEvents(ds, readable, events); | ||
return readable; | ||
} | ||
exports.createReadStream = createReadStream; | ||
function pipe(source, target, events) { | ||
proxyEvents(source, target, events || ['error']); | ||
return source.pipe(target); | ||
} | ||
exports.pipe = pipe; |
@@ -8,3 +8,3 @@ { | ||
"description": "SAP HANA Database Client for Node", | ||
"version": "0.0.5", | ||
"version": "0.1.0", | ||
"repository": { | ||
@@ -33,8 +33,13 @@ "type": "git", | ||
"devDependencies": { | ||
"should": "~1.2.2", | ||
"mocha": "~1.9.0", | ||
"should": "~2.1.0", | ||
"mocha": "~1.14.0", | ||
"async": "~0.2.9", | ||
"generic-pool": "~2.0.3" | ||
"generic-pool": "~2.0.4", | ||
"fstream": "~0.1.24", | ||
"concat-stream": "~1.2.0" | ||
}, | ||
"optionalDependencies": {} | ||
"optionalDependencies": { | ||
"debuglog": "~0.0.2", | ||
"readable-stream": "~1.1.9" | ||
} | ||
} |
109
README.md
@@ -12,7 +12,5 @@ SAP HANA Database Client for Node | ||
Install from [npm](https://npmjs.org/): | ||
Install from npm: | ||
```bash | ||
npm install hdb | ||
``` | ||
[![NPM](https://nodei.co/npm/hdb.png?compact=true)](https://npmjs.org/) | ||
@@ -39,11 +37,15 @@ or clone from the [GitHub repository](https://github.com/SAP/node-hdb) to run tests and examples locally: | ||
password : 'secret' | ||
}).connect(); | ||
}); | ||
client.exec('select * from DUMMY', function(err, rows) { | ||
client.connect(function (err) { | ||
if (err) { | ||
console.error('Error:', err); | ||
} else { | ||
return console.error('Connect error', err); | ||
} | ||
client.exec('select * from DUMMY', function (err, rows) { | ||
client.end(); | ||
if (err) { | ||
return console.error('Execute error:', err); | ||
} | ||
console.log('Results:', rows); | ||
} | ||
client.end(); | ||
}); | ||
}); | ||
@@ -74,18 +76,21 @@ ``` | ||
host : 'hostname', | ||
port : 30015 | ||
port : 30015, | ||
user : 'user', | ||
password : 'secret' | ||
}); | ||
client.connect({ | ||
user : 'user', | ||
password : 'secret' | ||
}, function(err) { | ||
user : 'somebody', | ||
password : 'abc123' | ||
}, function (err) { | ||
if (err) { | ||
console.error('Client connection error:', err); | ||
} else { | ||
console.log('Client connected!'); | ||
} | ||
return console.error('Client connection error:', err); | ||
} | ||
console.log('Client connected!'); | ||
}); | ||
``` | ||
If user and password are specified it will override the defaults of the client. It is possible to disconnect and reconnect with a different user on the same client instance and the same network connection. | ||
Direct Statement Execution | ||
Direct Statement Execution | ||
-------------------------- | ||
@@ -106,3 +111,3 @@ | ||
```js | ||
client.exec('create table TEST.NUMBERS (a int, b varchar(16))', function(err) { | ||
client.exec('create table TEST.NUMBERS (a int, b varchar(16))', function (err) { | ||
if (err) { | ||
@@ -120,3 +125,3 @@ return console.error('Error:', err); | ||
```js | ||
client.exec('insert into TEST.NUMBERS values (1, \'one\')', function(err, affectedRows) { | ||
client.exec('insert into TEST.NUMBERS values (1, \'one\')', function (err, affectedRows) { | ||
if (err) { | ||
@@ -131,4 +136,8 @@ return console.error('Error:', err); | ||
In the case of a Query all selected `rows` are fetched and returned in the callback. | ||
The client has two functions for query execution. | ||
#### `exec` | ||
The `exec` function is the short form. In this case all selected `rows` are fetched and returned in the callback. The `resultSet` is automatically closed and all `Lobs` are completely read and returned as Buffer objects. | ||
```js | ||
@@ -141,5 +150,25 @@ client.exec('select A, B from TEST.NUMBERS oder by A', function(err, rows) { | ||
}); | ||
``` | ||
#### `execute` | ||
The `execute` function is the long form. In this case the `resultSet` object is returned in the callback. The `resultSet` object allows you to create an object based `row` stream or an array based `rows` stream which can be piped to an writer object. Don't forget to close the resultset in this case. Take a look at the example `app4` for further details. | ||
```js | ||
client.execute('select A, B from TEST.NUMBERS oder by A', function(err, rs) { | ||
if (err) { | ||
return console.error('Error:', err); | ||
} | ||
rs.setFetchSize(2048); | ||
rs.createObjectStream() | ||
.pipe(new MyWriteStream()) | ||
.on('finish', function (){ | ||
if (!rs.closed) { | ||
rs.close(); | ||
} | ||
}); | ||
}); | ||
``` | ||
Prepared Statement Execution | ||
@@ -164,2 +193,4 @@ ---------------------------- | ||
The execution of a prepared statement is similar to the direct statement execution on the client. The difference is that the first parameter of `exec` function is an array with positional `parameters`. In case of named parameters it can also be an `parameters` object. | ||
```js | ||
@@ -174,2 +205,4 @@ statement.exec([1], function (err, rows) { | ||
If you use the `execute` instead of `exec` function the `resultSet` is returned in the callback like in direct query execution above. | ||
### Calling Stored Procedures | ||
@@ -222,6 +255,8 @@ | ||
} | ||
console.log('Statement droped'); | ||
console.log('Statement dropped'); | ||
}); | ||
``` | ||
The callback is optional in this case. | ||
Running tests | ||
@@ -242,5 +277,3 @@ ------------- | ||
For the acceptance tests a database connection has to be established. Therefore you | ||
need to copy the configuration template [config.tpl.json](./test/lib/config.tpl.json) | ||
in the ```test/lib``` folder to ```config.json``` and change the connection data to yours. | ||
For the acceptance tests a database connection has to be established. Therefore you need to copy the configuration template [config.tpl.json](https://github.com/SAP/node-hdb/blob/master/test/lib/config.tpl.json) in the ```test/lib``` folder to ```config.json``` and change the connection data to yours. | ||
@@ -251,8 +284,20 @@ | ||
Also, for the [examples](./examples) you need a valid a ```config.json``` in the ```test/lib``` folder. | ||
Also, for the examples you need a valid a ```config.json``` in the ```test/lib``` folder. | ||
The example for call procedure: | ||
- [app1](https://github.com/SAP/node-hdb/blob/master/examples/app1.js): Simple query. | ||
- [app2](https://github.com/SAP/node-hdb/blob/master/examples/app2.js): Fetch rows from `ResultSet`. | ||
- [app3](https://github.com/SAP/node-hdb/blob/master/examples/app3.js): Streaming rows `createObjectStream()`. | ||
- [app4](https://github.com/SAP/node-hdb/blob/master/examples/app4.js): Pipe row into JSON-Transform and to `stdout`. | ||
- [app5](https://github.com/SAP/node-hdb/blob/master/examples/app5.js): Stream XS repository into the filesystem. | ||
- [app6](https://github.com/SAP/node-hdb/blob/master/examples/app6.js): Stream from the filesystem into a db table. | ||
- [call1](https://github.com/SAP/node-hdb/blob/master/examples/call1.js): Call stored procedure. | ||
- [csv](https://github.com/SAP/node-hdb/blob/master/examples/csv.js): Stream a db table into csv file. | ||
- [server](https://github.com/SAP/node-hdb/blob/master/examples/server.js): Stream rows into http response `http://localhost:1337/{schema}/{tablename}?top={top}` | ||
To call e.g. the first example: | ||
```bash | ||
node examples/call1 | ||
node examples/app1 | ||
``` | ||
@@ -262,10 +307,8 @@ | ||
---- | ||
* Finish support for Lob data types | ||
* Support for read Lob in development | ||
* Support for write Lob not yet started | ||
* Transaction handling | ||
* Support for (streamed) WriteLob requests | ||
* Improve documentation of the client api | ||
* Improve error handling | ||
* SAML Authentication support | ||
* Transaction handling | ||
* Enhance tests | ||
* ... |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Uses eval
Supply chain riskPackage uses dynamic code execution (e.g., eval()), which is a dangerous practice. This can prevent the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Uses eval
Supply chain riskPackage uses dynamic code execution (e.g., eval()), which is a dangerous practice. This can prevent the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 1 instance in 1 package
208297
83
5881
302
2
6