Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Ability to Set Session Variables at Connect Time #189

Merged
merged 7 commits into from
Nov 29, 2021
28 changes: 26 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ Table of contents
* [Streaming Large Objects](#streaming-large-objects)
* [CESU-8 encoding support](#cesu-8-encoding-support)
* [TCP Keepalive](#tcp-keepalive)
* [Setting Session-Specific Client Information](#setting-session-specific-client-information)
* [Running tests](#running-tests)
* [Running examples](#running-examples)

Expand Down Expand Up @@ -113,15 +114,16 @@ client.connect(function (err) {
Establish a database connection
-------------------------------

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 any following connect calls on the created client instance. In case of network connection errors like a connection timeout or a database restart, you should register an error event handler in order to be able to handle these kinds of problems. If there are no error event handlers, errors will not be emitted.
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 any following connect calls on the created client instance. Options beginning with the prefix "SESSIONVARIABLE:" are used to set session-specific client information at connect time (see example of setting EXAMPLEKEY=EXAMPLEVALUE below). In case of network connection errors like a connection timeout or a database restart, you should register an error event handler in order to be able to handle these kinds of problems. If there are no error event handlers, errors will not be emitted.

```js
var hdb = require('hdb');
var client = hdb.createClient({
host : 'hostname',
port : 30015,
user : 'user',
password : 'secret'
password : 'secret',
'SESSIONVARIABLE:EXAMPLEKEY' : 'EXAMPLEVALUE'
});
client.on('error', function (err) {
console.error('Network connection error', err);
Expand Down Expand Up @@ -619,6 +621,25 @@ var client = hdb.createClient({
});

```
Setting Session-Specific Client Information
-------------

The client information is a list of session variables (defined in property-value pairs that are case sensitive) that an application can set on a client object. These variables can be set at connection time via "SESSIONVARIABLE:" prefixed options, or by using the setClientInfo method to specify a single property-value pair.

```js
var hdb = require('hdb');
var client = hdb.createClient({
host : 'hostname',
port : 30015,
user : 'user',
password : 'secret',
"SESSIONVARIABLE:EXAMPLEKEY1" : "EXAMPLEVALUE1"
});
client.setClientInfo("EXAMPLEKEY2", "EXAMPLEVALUE2");

```
Session variables set via the setClientInfo method will be sent to the server during the next execute, prepare, or fetch operation.


Running tests
-------------
Expand All @@ -638,6 +659,9 @@ make test
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.





Running examples
----------------

Expand Down
13 changes: 13 additions & 0 deletions lib/Client.js
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,19 @@ Client.prototype.execute = function execute(command, options, cb) {
}));
};

Client.prototype.setClientInfo = function setClientInfo(key, val) {
if(key == null || val == null || String(key).length == 0 || String(val).length == 0) {
var err = new Error('Invalid arguments for Client.setClientInfo()');
if (this.listeners('error').length) {
this.emit('error', err);
} else {
throw(err);
}
} else {
this._connection._setClientInfo(String(key), String(val));
}
};

Client.prototype._execute = function _execute(command, options, cb) {
var result = this._createResult(this._connection, options);
this._connection.executeDirect({
Expand Down
9 changes: 9 additions & 0 deletions lib/protocol/ClientInfo.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,15 @@
'use strict';

var common = require('./common');
var util = require('../util');
var MessageType = common.MessageType;

module.exports = ClientInfo;

function ClientInfo() {
this._properties = {};
this._updatedProperties = {};
this._OS_USER = util.os_user;
}

ClientInfo.prototype.setProperty = function setProperty(key, value) {
Expand All @@ -32,6 +34,13 @@ ClientInfo.prototype.getProperty = function getProperty(key) {
return this._properties[key];
};

ClientInfo.prototype.getOSUser = function getProperty(key) {
for(var key in this._properties) {
if(key === "APPLICATIONUSER") return this._properties[key];
}
return this._OS_USER; // default
};

ClientInfo.prototype.shouldSend = function shouldSend(messageType) {
switch (messageType) {
case MessageType.EXECUTE:
Expand Down
18 changes: 18 additions & 0 deletions lib/protocol/Connection.js
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,20 @@ Connection.prototype._createAuthenticationManager = auth.createManager;
Connection.prototype.connect = function connect(options, cb) {
var self = this;
var manager;
for(var key in options) {
if(key.toUpperCase().startsWith("SESSIONVARIABLE:")) {
var sv_key = key.substring(key.indexOf(":") + 1);
var sv_value = options[key];
if(sv_key && sv_key.length > 0 && sv_value && sv_value.length > 0) {
this._clientInfo.setProperty(sv_key, sv_value);
}
delete options[key];
}
}
this.connectOptions.setOptions([{
name : common.ConnectOption.OS_USER,
value : this._clientInfo.getOSUser()
}]);
if(options["disableCloudRedirect"] == true) {
this._redirectType = common.RedirectType.REDIRECTION_DISABLED;
}
Expand Down Expand Up @@ -658,6 +672,10 @@ Connection.prototype.setRedirectType = function setRedirectType(type) {
this._redirectType = type;
};

Connection.prototype._setClientInfo = function _setClientInfo(key, val) {
this._clientInfo.setProperty(key, val);
};

function ConnectionState() {
this.sessionId = -1;
this.packetCount = -1;
Expand Down
1 change: 1 addition & 0 deletions lib/protocol/common/ConnectOption.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ module.exports = {
ROW_AND_COLUMN_OPTIMIZED_FORMAT: 20,
IGNORE_UNKNOWN_PARTS: 21,
DATA_FORMAT_VERSION2: 23,
OS_USER: 32,
REDIRECTION_TYPE: 57,
REDIRECTED_HOST: 58,
REDIRECTED_PORT: 59,
Expand Down
1 change: 1 addition & 0 deletions lib/protocol/common/ConnectOptionType.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ ConnectOptionType[ConnectOption.USE_TRANSACTION_FLAGS_ONLY] = TypeCode.BOOLEAN;
ConnectOptionType[ConnectOption.ROW_AND_COLUMN_OPTIMIZED_FORMAT] = TypeCode.BOOLEAN;
ConnectOptionType[ConnectOption.IGNORE_UNKNOWN_PARTS] = TypeCode.BOOLEAN;
ConnectOptionType[ConnectOption.DATA_FORMAT_VERSION2] = TypeCode.INT;
ConnectOptionType[ConnectOption.OS_USER] = TypeCode.STRING;
ConnectOptionType[ConnectOption.REDIRECTION_TYPE] = TypeCode.INT;
ConnectOptionType[ConnectOption.REDIRECTED_HOST] = TypeCode.STRING;
ConnectOptionType[ConnectOption.REDIRECTED_PORT] = TypeCode.INT;
Expand Down
1 change: 1 addition & 0 deletions lib/protocol/part/ConnectOptions.js
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ ConnectOptions.prototype.KEYS = [
common.ConnectOption.SPLIT_BATCH_COMMANDS,
common.ConnectOption.DATA_FORMAT_VERSION,
common.ConnectOption.DATA_FORMAT_VERSION2,
common.ConnectOption.OS_USER,
common.ConnectOption.REDIRECTION_TYPE,
common.ConnectOption.REDIRECTED_HOST,
common.ConnectOption.REDIRECTED_PORT,
Expand Down
1 change: 1 addition & 0 deletions lib/util/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ Object.defineProperties(exports, {

exports.pid = process.pid;
exports._debuglog = util.debuglog;
exports.os_user = os.userInfo().username;

exports.debuglog = function debuglog() {
if (typeof exports._debuglog === 'function') {
Expand Down
45 changes: 45 additions & 0 deletions test/hdb.Client.js
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,51 @@ describe('hdb', function () {
});
});

it('should set session variables from connect properties', function (done) {
var client = new lib.Client();
client._connection = createConn();
function createConn() {
var connection = new lib.Connection();
connection._connect = function (options, connectListener) {
var socket = mock.createSocket(options);
util.setImmediate(connectListener);
return socket;
};
return connection;
}
var props = {'SESSIONVARIABLE:SESSVAR1' : 'TESTVAR1',
'SESSIONVARIABLE:SESSVAR2' : 'TESTVAR2'};
client.connect(props, function (err) {
client._connection.getClientInfo().shouldSend(lib.common.MessageType.EXECUTE).should.eql(true);
client._connection.getClientInfo().getProperty("SESSVAR1").should.equal("TESTVAR1");
client._connection.getClientInfo().getProperty("SESSVAR2").should.equal("TESTVAR2");
client._connection.send(new lib.request.Segment(lib.common.MessageType.EXECUTE), null);
client._connection.getClientInfo().shouldSend(lib.common.MessageType.EXECUTE).should.eql(false);
client._connection.getClientInfo().getProperty("SESSVAR1").should.equal("TESTVAR1");
client._connection.getClientInfo().getProperty("SESSVAR2").should.equal("TESTVAR2");
done();
});
});

it('should set session variables via setClientInfo', function (done) {
var client = new lib.Client();
client._connection.getClientInfo().shouldSend(lib.common.MessageType.EXECUTE).should.eql(false);

client.setClientInfo("VARKEY1", "VARVAL1");
client._connection.getClientInfo().shouldSend(lib.common.MessageType.EXECUTE).should.eql(true);
client._connection.getClientInfo().getProperty("VARKEY1").should.equal("VARVAL1");
client._connection.send(new lib.request.Segment(lib.common.MessageType.EXECUTE), null);
client._connection.getClientInfo().shouldSend(lib.common.MessageType.EXECUTE).should.eql(false);

client.setClientInfo("VARKEY2", "VARVAL2");
client._connection.getClientInfo().shouldSend(lib.common.MessageType.EXECUTE).should.eql(true);
client._connection.getClientInfo().getProperty("VARKEY1").should.equal("VARVAL1");
client._connection.getClientInfo().getProperty("VARKEY2").should.equal("VARVAL2");
client._connection.send(new lib.request.Segment(lib.common.MessageType.EXECUTE), null);
client._connection.getClientInfo().shouldSend(lib.common.MessageType.EXECUTE).should.eql(false);
done();
});

describe('#secure connection', function () {
var tcp = require('../lib/protocol/tcp');
var originalCreateSocket = tcp.createSocket;
Expand Down
2 changes: 1 addition & 1 deletion test/lib.Connection.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ describe('Lib', function () {
connection.getClientInfo().getUpdatedProperties().should.eql([]);
});

it('should sent client info parts for execute requests', function (done) {
it('should send client info parts for execute requests', function (done) {
var connection = createConnection();
connection.open({}, function () {
connection.getClientInfo().setProperty('LOCALE', 'en_US');
Expand Down