Comparing version 0.2.0 to 0.3.1
@@ -18,6 +18,4 @@ // Copyright 2013 SAP AG. | ||
exports.Client = lib.Client; | ||
exports.createClient = lib.createClient; | ||
exports.Stringifier = lib.Stringifier; | ||
exports.createJSONStringifier = lib.createJSONStringifier; | ||
exports.createClient = function createClient(options) { | ||
return new lib.Client(options); | ||
}; | ||
exports.createJSONStringifier = lib.createJSONStringifier; |
@@ -30,22 +30,10 @@ // Copyright 2013 SAP AG. | ||
var settings = this._settings = util.extend({ | ||
this._settings = util.extend({ | ||
fetchSize: 1024, | ||
holdCursorsOverCommit: true, | ||
scrollableCursor: true | ||
scrollableCursor: true, | ||
autoReconnect: false | ||
}, options); | ||
this._connection = new Connection(settings); | ||
this.setAutoCommit(true); | ||
var self = this; | ||
function onerror(err) { | ||
self.emit('error', err); | ||
} | ||
this._connection.on('error', onerror); | ||
function onclose(hadError) { | ||
self._connection.removeListener('error', onerror); | ||
self.emit('close', hadError); | ||
} | ||
this._connection.once('close', onclose); | ||
this._connection = this._createConnection(this._settings); | ||
this._addListeners(this._connection); | ||
} | ||
@@ -106,3 +94,3 @@ | ||
Client.prototype.get = function (key) { | ||
if (key === undefined) { | ||
if (util.isUndefined(key)) { | ||
return this._settings; | ||
@@ -114,3 +102,3 @@ } | ||
Client.prototype.set = function (key, value) { | ||
if (!value && typeof util.isObject(key)) { | ||
if (!value && util.isObject(key)) { | ||
this._settings = util.extend(this._settings, key); | ||
@@ -129,12 +117,30 @@ } else { | ||
var settings = this._settings; | ||
function addOption(name) { | ||
/* jshint validthis:true */ | ||
if (name in settings) { | ||
this[name] = settings[name]; | ||
} | ||
} | ||
var openOptions = { | ||
host: this._settings.host, | ||
port: this._settings.port | ||
host: settings.host, | ||
port: settings.port | ||
}; | ||
['pfx', 'key', 'cert', 'ca', 'passphrase', 'rejectUnauthorized', | ||
'secureProtocol' | ||
].forEach(addOption, openOptions); | ||
util.extend(openOptions, options); | ||
var authOptions = util.extend({ | ||
user: this._settings.user, | ||
password: this._settings.password | ||
}, options); | ||
var connectOptions = {}; | ||
['user', 'password', 'assertion', 'sessionCookie'].forEach(addOption, | ||
connectOptions); | ||
util.extend(connectOptions, options); | ||
// SAML assertion can only be used once | ||
if (this._settings.assertion) { | ||
this._settings.assertion = undefined; | ||
} | ||
var self = this; | ||
@@ -151,10 +157,26 @@ | ||
function authenticate() { | ||
self._connection.connect(authOptions, done); | ||
function onopen(err) { | ||
if (err) { | ||
return done(err); | ||
} | ||
self._connection.connect(connectOptions, done); | ||
} | ||
if (this._connection.readyState === 'new') { | ||
this._connection.open(openOptions, authenticate); | ||
this._connection.open(openOptions, onopen); | ||
} else if (this._connection.readyState === 'closed') { | ||
this._connection = this._createConnection(this._settings); | ||
this._addListeners(this._connection); | ||
this._connection.open(openOptions, onopen); | ||
} else if (this._connection.readyState === 'disconnected') { | ||
this._connection.connect(connectOptions, done); | ||
} else { | ||
authenticate(); | ||
if (util.isFunction(cb)) { | ||
util.setImmediate(function deferError() { | ||
var msg = util.format('Cannot connect in state "%s"', self.readyState); | ||
var err = new Error(msg); | ||
err.code = 'EHDBCONNECT'; | ||
cb(err); | ||
}); | ||
} | ||
} | ||
@@ -179,5 +201,6 @@ return this; | ||
Client.prototype.end = function end() { | ||
Client.prototype.close = function close() { | ||
this._connection.close(); | ||
}; | ||
Client.prototype.end = Client.prototype.close; | ||
@@ -195,5 +218,3 @@ Client.prototype.prepare = function prepare(command, cb) { | ||
var statement = new Statement(this._connection); | ||
this._connection.prepare({ | ||
command: command | ||
}, function onreply(err, reply) { | ||
this._connection.prepare(options, function onreply(err, reply) { | ||
statement.handle(err, reply, cb); | ||
@@ -204,2 +225,6 @@ }); | ||
Client.prototype.destroy = function destroy(err) { | ||
this._connection.destroy(err); | ||
}; | ||
Client.prototype.exec = function exec(command, options, cb) { | ||
@@ -221,2 +246,33 @@ var defaults = { | ||
Client.prototype._createConnection = function _createConnection(settings) { | ||
return new Connection(settings); | ||
}; | ||
Client.prototype._addListeners = function _addListeners(connection) { | ||
var self = this; | ||
function cleanup() { | ||
connection.removeListener('error', onerror); | ||
connection.removeListener('close', onclose); | ||
} | ||
function onerror(err) { | ||
self.emit('error', err); | ||
} | ||
connection.on('error', onerror); | ||
function onclose(hadError) { | ||
cleanup(); | ||
self.emit('close', hadError); | ||
if (hadError && self.get('autoReconnect')) { | ||
self.connect(); | ||
} | ||
} | ||
connection.on('close', onclose); | ||
}; | ||
Client.prototype._createResult = function _createResult(connection, options) { | ||
return new Result(connection, options); | ||
}; | ||
function executeDirect(defaults, command, options, cb) { | ||
@@ -237,3 +293,3 @@ /* jshint validthis: true */ | ||
}; | ||
var result = new Result(this._connection, options); | ||
var result = this._createResult(this._connection, options); | ||
@@ -240,0 +296,0 @@ function onreply(err, reply) { |
@@ -17,6 +17,22 @@ // Copyright 2013 SAP AG. | ||
var util = exports.util = require('./util'); | ||
util.extend(exports, require('./protocol')); | ||
exports.Client = require('./Client'); | ||
exports.createJSONStringifier = function createJSONStringifier() { | ||
return new exports.Stringifier({ | ||
var Client = exports.Client = require('./Client'); | ||
var protocol = require('./protocol'); | ||
util.extend(exports, protocol); | ||
exports.createJSONStringifier = createJSONStringifier; | ||
exports.createClient = createClient; | ||
exports.connect = connect; | ||
function connect(options, cb) { | ||
var client = createClient(options); | ||
client.connect(cb); | ||
return client; | ||
} | ||
function createClient(options) { | ||
return new Client(options); | ||
} | ||
function createJSONStringifier() { | ||
return new protocol.Stringifier({ | ||
header: '[', | ||
@@ -27,2 +43,2 @@ footer: ']', | ||
}); | ||
}; | ||
} |
@@ -16,2 +16,6 @@ // Copyright 2013 SAP AG. | ||
exports.SCRAMSHA256 = require('./scramsha256'); | ||
var Manager = require('./Manager'); | ||
exports.createManager = function createManager(options) { | ||
return new Manager(options); | ||
}; |
@@ -17,2 +17,3 @@ // Copyright 2013 SAP AG. | ||
var net = require('net'); | ||
var tls = require('tls'); | ||
var os = require('os'); | ||
@@ -47,2 +48,3 @@ var EventEmitter = require('events').EventEmitter; | ||
var self = this; | ||
// public | ||
@@ -59,9 +61,5 @@ this.clientId = [process.pid || 'nodejs', os.hostname()].join('@'); | ||
this._transaction = new Transaction(); | ||
var self = this; | ||
function onerror(err) { | ||
this._transaction.once('error', function onerror(err) { | ||
self.destroy(err); | ||
} | ||
this._transaction.once('error', onerror); | ||
}); | ||
} | ||
@@ -131,48 +129,49 @@ | ||
if (this._socket) { | ||
return cb(new Error('Call open only once')); | ||
util.setImmediate(function deferError() { | ||
cb(new Error('Call open only once')); | ||
}); | ||
return; | ||
} | ||
function done(err) { | ||
function done(err, protocolVersion) { | ||
if (err) { | ||
self.emit('error', err); | ||
} else { | ||
self.emit('open'); | ||
return cb(err); | ||
} | ||
if (util.isFunction(cb)) { | ||
cb(err); | ||
} | ||
} | ||
function onready(err, protocolVersion) { | ||
if (err) { | ||
return done(err); | ||
} | ||
self._protocolVersion = protocolVersion; | ||
self._initConnection(); | ||
done(null); | ||
self._addListeners(self._socket); | ||
self.emit('open'); | ||
cb(); | ||
} | ||
this._socket = this._createSocket(options); | ||
this._initSocket(this._socket, onready); | ||
this._socket = this._connect(options, done); | ||
}; | ||
Connection.prototype._createSocket = function _createSocket(options) { | ||
var socket = net.connect(util.extend(options, { | ||
allowHalfOpen: false | ||
})); | ||
Connection.prototype._connect = function _connect(options, cb) { | ||
var tcp; | ||
if ('key' in options || 'cert' in options || 'ca' in options || | ||
'pfx' in options) { | ||
tcp = tls; | ||
} else { | ||
tcp = net; | ||
} | ||
options.allowHalfOpen = false; | ||
var socket = tcp.connect(options); | ||
socket.setNoDelay(true); | ||
return socket; | ||
}; | ||
Connection.prototype._initSocket = function _initSocket(socket, cb) { | ||
function cleanup() { | ||
socket.removeListener('connect', onconnect); | ||
socket.removeListener('error', onerror); | ||
socket.removeListener('data', ondata); | ||
socket.removeListener('connect', onconnect); | ||
} | ||
function onconnect() { | ||
function onconnect(err) { | ||
socket.write(initializationRequestBuffer); | ||
} | ||
socket.on('connect', onconnect); | ||
socket.once('connect', onconnect); | ||
function onerror(err) { | ||
cleanup(); | ||
cb(err); | ||
} | ||
socket.on('error', onerror); | ||
function ondata(chunk) { | ||
@@ -187,15 +186,17 @@ cleanup(); | ||
} | ||
socket.once('data', ondata); | ||
socket.on('data', ondata); | ||
function onerror(err) { | ||
cleanup(); | ||
cb(err); | ||
} | ||
socket.on('error', onerror); | ||
return socket; | ||
}; | ||
Connection.prototype._initConnection = function _initConnection() { | ||
Connection.prototype._addListeners = function _addListeners(socket) { | ||
var self = this; | ||
var packet = new MessageBuffer(); | ||
function cleanup() { | ||
socket.removeListener('error', onerror); | ||
socket.removeListener('data', ondata); | ||
socket.removeListener('close', onclose); | ||
} | ||
// register listerners on socket | ||
@@ -217,3 +218,3 @@ function ondata(chunk) { | ||
} | ||
this._socket.on('data', ondata); | ||
socket.on('data', ondata); | ||
@@ -223,15 +224,19 @@ function onerror(err) { | ||
} | ||
this._socket.on('error', onerror); | ||
socket.on('error', onerror); | ||
function onclose(hadError) { | ||
self._socket.removeAllListeners(); | ||
self._socket = undefined; | ||
self._state = undefined; | ||
self._queue.abort(); | ||
self._queue = undefined; | ||
cleanup(); | ||
self._cleanup(); | ||
self.emit('close', hadError); | ||
} | ||
this._socket.once('close', onclose); | ||
socket.on('close', onclose); | ||
}; | ||
Connection.prototype._cleanup = function _cleanup() { | ||
this._socket = undefined; | ||
this._state = undefined; | ||
this._queue.abort(); | ||
this._queue = undefined; | ||
}; | ||
Connection.prototype.send = function send(message, receive) { | ||
@@ -243,3 +248,3 @@ if (this._statementContext) { | ||
debug('send', message); | ||
trace(SegmentKind.REQUEST, message); | ||
trace('REQUEST', message); | ||
@@ -296,3 +301,3 @@ var size = MAX_PACKET_SIZE - PACKET_HEADER_LENGTH; | ||
segment = ReplySegment.create(buffer, 0); | ||
trace(segment.kind, segment); | ||
trace(segment.kind === SegmentKind.ERROR ? 'ERROR' : 'REPLY', segment); | ||
reply = segment.getReply(); | ||
@@ -342,27 +347,12 @@ this.setStatementContext(reply.statementContext); | ||
var name = options.algorithm || 'SCRAMSHA256'; | ||
var authMethod = auth[name]; | ||
var algorithm = new authMethod.Algorithm(options.clientChallenge); | ||
var manager; | ||
try { | ||
manager = auth.createManager(options); | ||
} catch (err) { | ||
return util.setImmediate(function deferError() { | ||
cb(err); | ||
}); | ||
} | ||
var authOptions = { | ||
authentication: { | ||
user: options.user, | ||
algorithm: name, | ||
clientChallenge: algorithm.clientChallenge | ||
} | ||
}; | ||
var authMessage = request.authenticate(authMethod, authOptions); | ||
var connOptions = { | ||
authentication: { | ||
user: options.user, | ||
algorithm: name, | ||
clientProof: undefined | ||
}, | ||
clientId: this.clientId, | ||
connectOptions: this.connectOptions.getOptions() | ||
}; | ||
function connReceive(err, reply) { | ||
/* jshint validthis:true */ | ||
if (err) { | ||
@@ -374,2 +364,7 @@ return cb(err); | ||
} | ||
manager.finalize(reply.authentication); | ||
self._settings.user = manager.userFromServer; | ||
if (manager.sessionCookie) { | ||
self._settings.sessionCookie = manager.sessionCookie; | ||
} | ||
self._queue.resume(); | ||
@@ -380,14 +375,19 @@ cb(null, reply); | ||
function authReceive(err, reply) { | ||
/* jshint validthis:true */ | ||
if (err) { | ||
return cb(err); | ||
} | ||
var authReply = authMethod.Authentication.convert(reply.authentication); | ||
algorithm.salts = [authReply.salt]; | ||
algorithm.serverChallenge = authReply.serverChallenge; | ||
connOptions.authentication.clientProof = algorithm.getClientProof(options.password); | ||
var connMessage = request.connect(authMethod, connOptions); | ||
self.send(connMessage, connReceive); | ||
try { | ||
manager.initialize(reply.authentication); | ||
} catch (err) { | ||
return cb(err); | ||
} | ||
self.send(request.connect({ | ||
authentication: manager.finalData(), | ||
clientId: self.clientId, | ||
connectOptions: self.connectOptions.getOptions() | ||
}), connReceive); | ||
} | ||
this.send(authMessage, authReceive); | ||
this.send(request.authenticate({ | ||
authentication: manager.initialData() | ||
}), authReceive); | ||
}; | ||
@@ -402,3 +402,3 @@ | ||
} | ||
self._statementContext = new part.StatementContext(); | ||
self._statementContext = undefined; | ||
self._state = new ConnectionState(); | ||
@@ -567,8 +567,7 @@ cb(null, reply); | ||
Connection.prototype.close = function close() { | ||
var socket = this._socket; | ||
var self = this; | ||
function closeConnection() { | ||
debug('close'); | ||
socket.readable = false; | ||
socket.end(); | ||
self.destroy(); | ||
} | ||
@@ -582,8 +581,5 @@ if (this._queue.empty && !this._queue.busy) { | ||
Connection.prototype.destroy = function destroy(err) { | ||
var socket = this._socket; | ||
function destroySocket() { | ||
socket.destroy(err); | ||
if (this._socket) { | ||
this._socket.destroy(err); | ||
} | ||
process.nextTick(destroySocket); | ||
}; | ||
@@ -590,0 +586,0 @@ |
@@ -34,3 +34,3 @@ // Copyright 2013 SAP AG. | ||
offset += 1; | ||
if (fieldLength === 0xff) { | ||
if (fieldLength > 245) { | ||
fieldLength = buffer.readUInt16LE(offset); | ||
@@ -69,7 +69,7 @@ offset += 2; | ||
fieldLength = data.length; | ||
if (fieldLength <= 250) { | ||
if (fieldLength <= 245) { | ||
buffer[offset] = fieldLength; | ||
offset += 1; | ||
} else { | ||
buffer[offset] = 0xff; | ||
buffer[offset] = 0xf6; | ||
offset += 1; | ||
@@ -92,3 +92,3 @@ buffer.writeUInt16LE(fieldLength, offset); | ||
fieldLength = getByteLengthOfField(fields[i]); | ||
if (fieldLength <= 250) { | ||
if (fieldLength <= 245) { | ||
byteLength += fieldLength + 1; | ||
@@ -95,0 +95,0 @@ } else { |
@@ -22,2 +22,3 @@ // Copyright 2013 SAP AG. | ||
exports.Stringifier = require('./Stringifier'); | ||
exports.Transaction = require('./Transaction'); | ||
exports.Reader = require('./Reader'); | ||
@@ -24,0 +25,0 @@ exports.Writer = require('./Writer'); |
@@ -71,7 +71,7 @@ // Copyright 2013 SAP AG. | ||
Reader.prototype.readString = function readString() { | ||
this.readBytes('utf-8'); | ||
return this.readBytes('utf-8'); | ||
}; | ||
Reader.prototype.readBinary = function readBinary() { | ||
this.readBytes(); | ||
return this.readBytes(); | ||
}; | ||
@@ -106,19 +106,21 @@ | ||
/* jshint bitwise:false */ | ||
if (!(this.buffer[this.offset + 1] & 0x80)) { | ||
var high = this.buffer[this.offset + 1]; | ||
// msb not set ==> null | ||
if (!(high & 0x80)) { | ||
this.offset += 4; | ||
return null; | ||
} | ||
var year = this.buffer.readInt16LE(this.offset, true); | ||
if (year & 0x8000) { | ||
year = year & 0x7fff; | ||
} | ||
if (year & 0x4000) { | ||
year = year | 0x8000; | ||
} | ||
var month = this.buffer.readInt8(this.offset + 2, true) + 1; | ||
var day = this.buffer.readInt8(this.offset + 3, true); | ||
this.offset += 4; | ||
return bignum.lpad4(year) + '-' + | ||
bignum.lpad2(month) + '-' + | ||
bignum.lpad2(day); | ||
var year = this.buffer[this.offset]; | ||
this.offset += 2; | ||
var month = this.buffer[this.offset] + 1; | ||
this.offset += 1; | ||
var day = this.buffer[this.offset]; | ||
this.offset += 1; | ||
// msb set ==> not null | ||
// unset msb and second most sb | ||
high &= 0x3f; | ||
year |= high << 8; | ||
return util.lpad4(year) + '-' + | ||
util.lpad2(month) + '-' + | ||
util.lpad2(day); | ||
}; | ||
@@ -128,16 +130,18 @@ | ||
/* jshint bitwise:false */ | ||
if (!(this.buffer[this.offset] & 0x80)) { | ||
var hour = this.buffer[this.offset]; | ||
// msb not set ==> null | ||
if (!(hour & 0x80)) { | ||
this.offset += 4; | ||
return null; | ||
} | ||
var hour = this.buffer.readInt8(this.offset, true); | ||
if (hour & 0x80) { | ||
hour = hour & 0x7f; | ||
} | ||
var min = this.buffer.readInt8(this.offset + 1, true); | ||
var msec = this.buffer.readUInt16LE(this.offset + 2, true); | ||
this.offset += 4; | ||
return bignum.lpad2(hour) + ':' + | ||
bignum.lpad2(min) + ':' + | ||
bignum.lpad2(msec / 1000); | ||
var min = this.buffer[this.offset + 1]; | ||
this.offset += 2; | ||
var msec = this.buffer.readUInt16LE(this.offset, true); | ||
this.offset += 2; | ||
// msb set ==> not null | ||
// unset msb | ||
hour &= 0x7f; | ||
return util.lpad2(hour) + ':' + | ||
util.lpad2(min) + ':' + | ||
util.lpad2(msec / 1000); | ||
}; | ||
@@ -148,11 +152,12 @@ | ||
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; | ||
if (date) { | ||
if (time) { | ||
return date + 'T' + time; | ||
} | ||
return date + 'T00:00:00'; | ||
} | ||
if (time) { | ||
return '0001-01-01T' + time; | ||
} | ||
return null; | ||
}; | ||
@@ -181,3 +186,3 @@ | ||
this.offset += 8; | ||
if (value === 315538070401) { | ||
if (value === 315538070401 || value === 0) { | ||
return null; | ||
@@ -191,3 +196,3 @@ } | ||
this.offset += 8; | ||
if (value === '3155380704000000001') { | ||
if (value === '3155380704000000001' || value === 0) { | ||
return null; | ||
@@ -197,3 +202,3 @@ } | ||
var index = value.length - 7; | ||
return value.substring(0, index) + bignum.lpad7(value.substring(index) - | ||
return value.substring(0, index) + util.lpad7(value.substring(index) - | ||
1); | ||
@@ -200,0 +205,0 @@ } else { |
@@ -87,19 +87,13 @@ // Copyright 2013 SAP AG. | ||
function authenticate(method, options) { | ||
function authenticate(options) { | ||
var segment = createSegment(MessageType.AUTHENTICATE, options); | ||
// authentication | ||
segment.add({ | ||
kind: PartKind.AUTHENTICATION, | ||
module: method.Authentication | ||
}, options.authentication); | ||
segment.add(PartKind.AUTHENTICATION, options.authentication); | ||
return segment; | ||
} | ||
function connect(method, options) { | ||
function connect(options) { | ||
var segment = createSegment(MessageType.CONNECT, options); | ||
// authentication | ||
segment.add({ | ||
kind: PartKind.AUTHENTICATION, | ||
module: method.Connect | ||
}, options.authentication); | ||
segment.add(PartKind.AUTHENTICATION, options.authentication); | ||
// clientId | ||
@@ -106,0 +100,0 @@ segment.add(PartKind.CLIENT_ID, options.clientId); |
@@ -22,3 +22,2 @@ // Copyright 2013 SAP AG. | ||
var TypeCode = common.TypeCode; | ||
var FunctionCode = common.FunctionCode; | ||
@@ -31,3 +30,3 @@ | ||
this._connection = connection; | ||
this._resultSetMode = options.resultSetMode || options.autoFetch === false; | ||
this._autoFetch = !! options.autoFetch; | ||
this._readSize = options.readSize || Lob.DEFAULT_READ_SIZE; | ||
@@ -99,3 +98,3 @@ this._resultSetMetadata = undefined; | ||
if (this._resultSetMode) { | ||
if (!this._autoFetch) { | ||
return done(null, resultSets); | ||
@@ -118,3 +117,3 @@ } | ||
if (this._resultSetMode) { | ||
if (!this._autoFetch) { | ||
return done(null, resultSets); | ||
@@ -121,0 +120,0 @@ } |
@@ -257,5 +257,2 @@ // Copyright 2013 SAP AG. | ||
} | ||
if (!reply.resultSets.length) { | ||
console.log(err, reply); | ||
} | ||
var data = reply.resultSets[0].data; | ||
@@ -262,0 +259,0 @@ if (this._running) { |
@@ -43,6 +43,4 @@ // Copyright 2013 SAP AG. | ||
this.push(this.transformRows(thing)); | ||
} else if (util.isObject(thing)) { | ||
} else { | ||
this.push(this.transformRow(thing)); | ||
} else { | ||
this.push(''); | ||
} | ||
@@ -49,0 +47,0 @@ done(); |
@@ -62,7 +62,4 @@ // Copyright 2013 SAP AG. | ||
this.error.fatal = true; | ||
var self = this; | ||
process.nextTick(function emitError() { | ||
self.emit('error', self.error); | ||
}); | ||
this.emit('error', this.error); | ||
} | ||
}; |
@@ -17,33 +17,6 @@ // Copyright 2013 SAP AG. | ||
var INT_10_1 = Math.pow(10, 1); | ||
var INT_10_2 = Math.pow(10, 2); | ||
var zeropad = require('./zeropad'); | ||
var INT_10_3 = Math.pow(10, 3); | ||
var INT_10_4 = Math.pow(10, 4); | ||
var INT_10_5 = Math.pow(10, 5); | ||
var INT_10_6 = Math.pow(10, 6); | ||
var INT_10_7 = Math.pow(10, 7); | ||
var INT_10_8 = Math.pow(10, 8); | ||
var INT_10_9 = Math.pow(10, 9); | ||
var INT_10_10 = Math.pow(10, 10); | ||
var INT_10_11 = Math.pow(10, 11); | ||
var INT_10_12 = Math.pow(10, 12); | ||
var INT_10_13 = Math.pow(10, 13); | ||
var BASE = Math.pow(10, 7); | ||
var ZERO_1 = '0'; | ||
var ZERO_2 = '00'; | ||
var ZERO_3 = '000'; | ||
var ZERO_4 = '0000'; | ||
var ZERO_5 = '00000'; | ||
var ZERO_6 = '000000'; | ||
var ZERO_7 = '0000000'; | ||
var ZERO_8 = '00000000'; | ||
var ZERO_9 = '000000000'; | ||
var ZERO_10 = '0000000000'; | ||
var ZERO_11 = '00000000000'; | ||
var ZERO_12 = '000000000000'; | ||
var ZERO_13 = '0000000000000'; | ||
var BASE = INT_10_7; | ||
var EXP_BIAS = 6176; | ||
@@ -103,52 +76,2 @@ | ||
/* Decimal zero padding */ | ||
var MAX_DECIMAL_LENGTH = 35; | ||
var ZEROS = ['']; | ||
for (var i = 1; i < MAX_DECIMAL_LENGTH; i++) { | ||
ZEROS.push(ZEROS[i - 1] + '0'); | ||
} | ||
function lpad14(number) { | ||
/* jshint curly: false */ | ||
if (number >= INT_10_13) return number; | ||
if (number >= INT_10_12) return ZERO_1 + number; | ||
if (number >= INT_10_11) return ZERO_2 + number; | ||
if (number >= INT_10_10) return ZERO_3 + number; | ||
if (number >= INT_10_9) return ZERO_4 + number; | ||
if (number >= INT_10_8) return ZERO_5 + number; | ||
if (number >= INT_10_7) return ZERO_6 + number; | ||
if (number >= INT_10_6) return ZERO_7 + number; | ||
if (number >= INT_10_5) return ZERO_8 + number; | ||
if (number >= INT_10_4) return ZERO_9 + number; | ||
if (number >= INT_10_3) return ZERO_10 + number; | ||
if (number >= INT_10_2) return ZERO_11 + number; | ||
if (number >= INT_10_1) return ZERO_12 + number; | ||
return ZERO_13 + number; | ||
} | ||
function lpad7(number) { | ||
/* jshint curly: false */ | ||
if (number >= INT_10_6) return number; | ||
if (number >= INT_10_5) return ZERO_1 + number; | ||
if (number >= INT_10_4) return ZERO_2 + number; | ||
if (number >= INT_10_3) return ZERO_3 + number; | ||
if (number >= INT_10_2) return ZERO_4 + number; | ||
if (number >= INT_10_1) return ZERO_5 + number; | ||
return ZERO_6 + number; | ||
} | ||
function lpad4(number) { | ||
/* jshint curly: false */ | ||
if (number >= INT_10_3) return number; | ||
if (number >= INT_10_2) return ZERO_1 + number; | ||
if (number >= INT_10_1) return ZERO_2 + number; | ||
return ZERO_3 + number; | ||
} | ||
function lpad2(number) { | ||
/* jshint curly: false */ | ||
if (number >= INT_10_1) return number; | ||
return ZERO_1 + number; | ||
} | ||
function _readInt64(buffer, offset, unsigned) { | ||
@@ -218,5 +141,5 @@ | ||
if (s === 1) { | ||
return '' + x2 + lpad14(x1 * BASE + x0); | ||
return '' + x2 + zeropad.lpad14(x1 * BASE + x0); | ||
} | ||
return '-' + x2 + lpad14(x1 * BASE + x0); | ||
return '-' + x2 + zeropad.lpad14(x1 * BASE + x0); | ||
@@ -386,7 +309,8 @@ } | ||
if (x4) { | ||
dec.m = '' + x4 + lpad14(x3 * BASE + x2) + lpad14(x1 * BASE + x0); | ||
dec.m = '' + x4 + zeropad.lpad14(x3 * BASE + x2) + zeropad.lpad14(x1 * BASE + | ||
x0); | ||
return dec; | ||
} | ||
if (x3) { | ||
dec.m = '' + (x3 * BASE + x2) + lpad14(x1 * BASE + x0); | ||
dec.m = '' + (x3 * BASE + x2) + zeropad.lpad14(x1 * BASE + x0); | ||
return dec; | ||
@@ -400,3 +324,3 @@ } | ||
} | ||
dec.m = '' + x2 + lpad14(x1 * BASE + x0); | ||
dec.m = '' + x2 + zeropad.lpad14(x1 * BASE + x0); | ||
return dec; | ||
@@ -452,3 +376,3 @@ } | ||
i = '0'; | ||
f = zeros(-k) + d; | ||
f = zeropad.ZEROS[-k] + d; | ||
} else { | ||
@@ -459,3 +383,3 @@ i = '0'; | ||
} else if (e > 0) { | ||
i = d + zeros(e); | ||
i = d + zeropad.ZEROS[e]; | ||
f = ''; | ||
@@ -476,3 +400,3 @@ } else { | ||
} else if (l < frac) { | ||
f += zeros(frac - l); | ||
f += zeropad.ZEROS[frac - l]; | ||
} | ||
@@ -482,12 +406,2 @@ return i + '.' + f; | ||
function zeros(l) { | ||
if (l < MAX_DECIMAL_LENGTH) { | ||
return ZEROS[l]; | ||
} | ||
var z = ''; | ||
for (var i = 0; i < l; i++) { | ||
z += '0'; | ||
} | ||
return z; | ||
} | ||
@@ -705,6 +619,7 @@ function _writeInt64(buffer, value, offset, unsigned) { | ||
if (x4) { | ||
return '' + x4 + lpad14(x3 * BASE + x2) + lpad14(x1 * BASE + x0); | ||
return '' + x4 + zeropad.lpad14(x3 * BASE + x2) + zeropad.lpad14(x1 * BASE + | ||
x0); | ||
} | ||
if (x3) { | ||
return '' + (x3 * BASE + x2) + lpad14(x1 * BASE + x0); | ||
return '' + (x3 * BASE + x2) + zeropad.lpad14(x1 * BASE + x0); | ||
} | ||
@@ -716,3 +631,3 @@ if (x2) { | ||
} | ||
return '' + x2 + lpad14(x1 * BASE + x0); | ||
return '' + x2 + zeropad.lpad14(x1 * BASE + x0); | ||
@@ -906,6 +821,2 @@ } | ||
exports.readDecFloat = readDecFloat; | ||
exports.readDecFixed = readDecFixed; | ||
exports.lpad2 = lpad2; | ||
exports.lpad4 = lpad4; | ||
exports.lpad7 = lpad7; | ||
exports.lpad14 = lpad14; | ||
exports.readDecFixed = readDecFixed; |
@@ -61,2 +61,7 @@ // Copyright 2013 SAP AG. | ||
exports.bignum = require('./bignum'); | ||
exports.calendar = require('./calendar'); | ||
exports.Queue = require('./Queue'); | ||
extend(exports, require('./zeropad')); | ||
var debuglog; | ||
@@ -78,3 +83,2 @@ if (util.debuglog) { | ||
var traceFunction; | ||
exports.tracelog = function tracelog() { | ||
@@ -84,8 +88,10 @@ if (typeof traceFunction !== 'function') { | ||
var timestamp = Math.floor(Date.now() / 1000); | ||
var filename = path.join(os.tmpDir(), 'hdb.trace.' + timestamp); | ||
var filename = path.join(os.tmpDir(), 'hdb.trace.' + timestamp + '.log'); | ||
console.log('Trace to file', filename); | ||
traceFunction = function trace(kind, segment) { | ||
fs.appendFileSync(filename, util.inspect(segment, { | ||
depth: 9 | ||
})); | ||
fs.appendFileSync(filename, | ||
kind + '\n' + | ||
util.inspect(segment, { | ||
depth: 9 | ||
})); | ||
}; | ||
@@ -99,6 +105,2 @@ } else { | ||
exports.bignum = require('./bignum'); | ||
exports.Queue = require('./Queue'); | ||
function extend(obj) { | ||
@@ -105,0 +107,0 @@ function extendOnce(source) { |
@@ -8,3 +8,3 @@ { | ||
"description": "SAP HANA Database Client for Node", | ||
"version": "0.2.0", | ||
"version": "0.3.1", | ||
"repository": { | ||
@@ -33,12 +33,12 @@ "type": "git", | ||
"devDependencies": { | ||
"should": "~2.1.0", | ||
"mocha": "~1.14.0", | ||
"async": "~0.2.9", | ||
"debuglog": "~0.0.2", | ||
"readable-stream": "~1.1.9", | ||
"should": "~3.1.3", | ||
"mocha": "~1.17.1", | ||
"async": "~0.2.10", | ||
"debuglog": "~1.0.0", | ||
"readable-stream": "~1.1.11", | ||
"generic-pool": "~2.0.4", | ||
"fstream": "~0.1.24", | ||
"concat-stream": "~1.2.0" | ||
"fstream": "~0.1.25", | ||
"concat-stream": "~1.4.1" | ||
}, | ||
"optionalDependencies": {} | ||
} |
130
README.md
@@ -7,4 +7,19 @@ SAP HANA Database Client for Node | ||
[![Build Status](https://secure.travis-ci.org/SAP/node-hdb.png)](http://travis-ci.org/SAP/node-hdb) | ||
[![NPM](https://nodei.co/npm/hdb.png?compact=true)](https://npmjs.org/package/hdb) [![Build Status](https://secure.travis-ci.org/SAP/node-hdb.png)](http://travis-ci.org/SAP/node-hdb) | ||
Table of contents | ||
------------- | ||
* [Install](#install) | ||
* [Getting started](#getting-started) | ||
* [Establish a database connection](#establish-a-database-connection) | ||
* [Direct Statement Execution](#direct-statement-execution) | ||
* [Prepared Statement Execution](#prepared-statement-execution) | ||
* [Streaming results](#streaming-results) | ||
* [Transaction handling](#transaction-handling) | ||
* [Streaming Large Objects](#streaming-large-objects) | ||
* [Running tests](#running-tests) | ||
* [Running examples](#running-examples) | ||
* [Todo](#todo) | ||
Install | ||
@@ -15,3 +30,5 @@ ------- | ||
[![NPM](https://nodei.co/npm/hdb.png?compact=true)](https://npmjs.org/package/hdb) | ||
```bash | ||
npm install hdb | ||
``` | ||
@@ -26,7 +43,9 @@ or clone from the [GitHub repository](https://github.com/SAP/node-hdb) to run tests and examples locally: | ||
Introduction | ||
Getting started | ||
------------ | ||
A very simple example how to use this module: | ||
If you do not have access to a SAP HANA server, go to the [SAP HANA Developer Center](http://scn.sap.com/community/developer-center/hana) and choose one of the options to [get your own trial SAP HANA Server](http://scn.sap.com/docs/DOC-31722). | ||
This is a very simple example how to use this module: | ||
```js | ||
@@ -55,20 +74,7 @@ var hdb = require('hdb'); | ||
Authentication methods | ||
---------------------- | ||
Establish a database connection | ||
------------------------------- | ||
The SAP HANA Database supports the following authentication methods: | ||
The first step to establish a database connection is to create a client object. It is recommended to pass all required `connect` options like `host`, `port`, `user` and `password` to the `createClient` function. They will be used as defaults for following connect calls on the created client instance. | ||
- **SCRAMSHA256** user/password based authentication method | ||
- _GSS_ | ||
- _SAML_ | ||
Currently only the SCRAMSHA256 authentication method is supported. | ||
Establishing a connection to the database | ||
----------------------------------------- | ||
In order to be able to handle connection errors it is recommended to explicitly | ||
establish a connection using the `connect` method of the client object. | ||
```js | ||
@@ -80,19 +86,66 @@ var hdb = require('hdb'); | ||
user : 'user', | ||
password : 'secret' | ||
password : 'secret' | ||
}); | ||
console.log(client.readyState); // new | ||
``` | ||
client.connect({ | ||
user : 'somebody', | ||
password : 'abc123' | ||
}, function (err) { | ||
When a client instance is created it does not immediately open a network connection to the database host. Initially the client is in state `new`. When you call `connect` the first time two things are done internally. | ||
1. A network connection is established and the communication is initialized (Protocol - and Product Version exchange). Now the connection is ready for exchanging messages but no user session is established. The client is in state `disconnected`. This step is skipped if the client is already in state `disconnected`. | ||
2. The authentication process is initiated. After a successful user authentication a database session is established and the client is in state `connected`. If authentication fails the client remains in state `'disconnect'`. | ||
```js | ||
client.connect(function (err) { | ||
if (err) { | ||
return console.error('Client connection error:', err); | ||
return console.error('Error:', err); | ||
} | ||
console.log('Client connected!'); | ||
console.log(client.readyState); // 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. | ||
If user and password are specified they 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. | ||
### Authentication mechanisms | ||
Details about the different authentication method can be found in the [SAP HANA Security Guide](http://help.sap.com/hana/SAP_HANA_Security_Guide_en.pdf). | ||
#### User / Password | ||
Users authenticate themselves with their database `user` and `password`. | ||
#### SAML assertion | ||
SAML bearer assertions as well as unsolicited SAML responses that include an | ||
unencrypted SAML assertion can be used to authenticate users. SAML assertions and responses must be signed using XML signatures. XML Digital signatures can be created with [xml-crypto](https://www.npmjs.org/package/xml-crypto) or [xml-dsig](https://www.npmjs.org/package/xml-dsig). | ||
Instead of `user` and `password` you have to provide a SAML `assertion`. | ||
```js | ||
client.connect({ | ||
assertion: '<Assertion xmlns="urn:oasis:names:tc:SAML:2.0:assertion" ...>...</Assertion>' | ||
},function (err) { | ||
if (err) { | ||
return console.error('Error:', err); | ||
} | ||
console.log('User:', client.get('user')); | ||
console.log('SessionCookie:', client.get('SessionCookie')); | ||
}); | ||
``` | ||
After a successful SAML authentication the server returns the database `user` and a `SessionCookie` which can be used for reconnect. | ||
#### Kerberos | ||
A Kerberos authentication provider can be used to authenticate users. | ||
> This mechanism is currently not implemented. Please contact me if you require Kerberos based authentication. | ||
### Encrypted network communication | ||
To establish an encrypted database connection just pass whether `key`, `cert` and `ca` or a `pfx` to createClient. | ||
```js | ||
var client = hdb.createClient({ | ||
host : 'hostname', | ||
port : 30015, | ||
key : fs.readFileSync('client-key.pem'), | ||
cert : fs.readFileSync('client-cert.pem'), | ||
ca : [fs.readFileSync('trusted-cert.pem')], | ||
... | ||
}); | ||
``` | ||
Direct Statement Execution | ||
Direct Statement Execution | ||
-------------------------- | ||
@@ -103,5 +156,2 @@ | ||
Generally we return the statement execution results using callbacks. | ||
For callbacks we follow the convention described in the | ||
[Node.js Style Guide](http://nodeguide.com/style.html#callbacks) | ||
to reserve the first parameter of any callback for an optional error object. | ||
The type of returned result depends on the kind of statement. | ||
@@ -137,3 +187,3 @@ | ||
The `exec` function is a convinient way to completely retrieve the result of a query. 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. If streaming of the results is required you will have to use the `execute` function. This is described in section [Streaming results](#streaming-results). | ||
The `exec` function is a convenient way to completely retrieve the result of a query. 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. If streaming of the results is required you will have to use the `execute` function. This is described in section [Streaming results](#streaming-results). | ||
@@ -303,3 +353,3 @@ ```js | ||
Streaming Large Objects (LOBs) | ||
Streaming Large Objects | ||
------------- | ||
@@ -332,3 +382,3 @@ | ||
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. If the ```config.json``` file does not exist a local mock server is started. | ||
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/db/config.tpl.json) in the ```test/db``` folder to ```config.json``` and change the connection data to yours. If the ```config.json``` file does not exist a local mock server is started. | ||
@@ -339,3 +389,3 @@ | ||
Also, for the 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/db``` folder. | ||
@@ -350,2 +400,3 @@ | ||
- [app7](https://github.com/SAP/node-hdb/blob/master/examples/app7.js): Insert a row with a large image into a db table (uses WriteLobRequest and Transaction internally). | ||
- [app8](https://github.com/SAP/node-hdb/blob/master/examples/app8.js): Automatic reconnect when network connection is lost. | ||
- [call1](https://github.com/SAP/node-hdb/blob/master/examples/call1.js): Call stored procedure. | ||
@@ -357,3 +408,3 @@ - [call2](https://github.com/SAP/node-hdb/blob/master/examples/call2.js): Call stored procedure with lob input and output parameter. | ||
To call e.g. the first example: | ||
To run e.g. the first example: | ||
@@ -368,4 +419,3 @@ ```bash | ||
* Improve error handling | ||
* SAML Authentication support | ||
* Enhance tests | ||
* Increase test coverage | ||
* ... |
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
Network access
Supply chain riskThis module accesses the network.
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
251673
86
6948
409
3