Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions .github/workflows/backport.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: Backport labeled PRs

# When a PR is merged with a label of the form `backport rel/<branch>`,
# cherry-pick the merged commit onto the target release branch and open
# a backport PR.
#
# Example: label `backport rel/2.29` on a PR against `main` will open a
# PR containing the cherry-picked commit against `rel/2.29`.

on:
pull_request_target:
types: [closed, labeled]

jobs:
backport:
# Skip early when this event can't produce a backport: the PR isn't
# merged, or none of its labels look like a backport label. The
# backport-action itself would filter these out later, but this
# avoids spinning up a runner in the first place.
if: >
github.event.pull_request.merged &&
contains(toJSON(github.event.pull_request.labels.*.name), '"backport rel/')
runs-on: [self-hosted, solinas]
permissions:
contents: write
pull-requests: write
steps:
# WARNING: this workflow runs on `pull_request_target` with write
# permissions. Do NOT check out the PR's head ref here — only the
# base repo. All PR-controlled inputs (title, body) are passed as
# action inputs, never as shell arguments.
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3

- name: Create backport PRs
uses: korthout/backport-action@2e830a1d0b8269505846ddd407a70876913ad1f8 # v4.6.0
with:
label_pattern: '^backport (rel/[^ ]+)$'
pull_title: '${pull_title} [backport ${target_branch}]'
pull_description: |
Backport of #${pull_number} to `${target_branch}`.
60 changes: 60 additions & 0 deletions .github/workflows/integration-test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
name: Node-hdb Integration Test
run-name: "Integration Test — ${{ github.event.pull_request.title || github.ref_name }}"
on:
push:
branches: [main, 'rel/*']
pull_request:
branches: [main, 'rel/*']
workflow_dispatch:
concurrency:
group: integration-test
cancel-in-progress: false
jobs:
Node-hdb-Integration-Test:
runs-on: [self-hosted, solinas]
strategy:
matrix:
node-version: ['20', '22', '24', '26']
fail-fast: false
max-parallel: 1
permissions:
contents: write
id-token: write
steps:
- run: echo "🎉 The job was automatically triggered by a ${{ github.event_name }} event."
- run: echo "🐧 This job is now running on a ${{ runner.os }} SUGAR runner!"
- run: echo "🔎 The name of your branch is ${{ github.ref }} and your repository is ${{ github.repository }}."
- name: Check out repository code
uses: actions/checkout@v6
- name: Set up Node.js environment
uses: actions/setup-node@v6
with:
node-version: ${{ matrix.node-version }}
- run: echo "💡 The ${{ github.repository }} repository has been cloned to the runner."
- name: Create test/db/config.json
run: |
cat > ${{ github.workspace }}/test/db/config.json << EOF
{
"host": "${{ secrets.HDB_HOST }}",
"port": ${{ secrets.HDB_PORT }},
"user": "${{ secrets.HDB_USER }}",
"password": "${{ secrets.HDB_PASSWORD }}",
"proxyHostname": null,
"proxyPassword": null,
"proxyUserName": null
}
EOF
- run: echo "🖥️ The workflow is now ready to test your code on the runner."
- name: List files in the repository
run: |
echo "Workspace is ${{ github.workspace }}"
echo "Listing files in the repository:"
ls -l ${{ github.workspace }}
echo "Showing the contents of test/db/config.json:"
cat ${{ github.workspace }}/test/db/config.json
- run: npm install
- name: Run tests
run: |
echo "Running tests..."
make test
- run: echo "🍏 This job's status is ${{ job.status }}."
11 changes: 11 additions & 0 deletions .github/workflows/piper.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
name: Piper
on:
workflow_dispatch:

jobs:
piper:
uses: "project-piper/piper-pipeline-github/.github/workflows/sap-piper-workflow.yml@v1"
secrets: inherit
permissions:
contents: write
id-token: write
2 changes: 1 addition & 1 deletion cfg/VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
2.29.5
2.29.6
4 changes: 4 additions & 0 deletions lib/Client.js
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,10 @@ Client.prototype.setClientInfo = function setClientInfo(key, val) {
}
};

Client.prototype.isValid = function isValid(options, cb) {
this._connection.isValid(options, cb);
};

Client.prototype._execute = function _execute(command, options, cb) {
var result = this._createResult(this._connection, options);
this._connection.executeDirect({
Expand Down
19 changes: 16 additions & 3 deletions lib/protocol/ClientInfo.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@
// language governing permissions and limitations under the License.
'use strict';

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

module.exports = ClientInfo;

Expand Down Expand Up @@ -75,3 +76,15 @@ ClientInfo.prototype.getUpdatedProperties = function getUpdatedProperties() {
return res;
};

ClientInfo.prototype.getUpdatedPropertiesSize = function getUpdatedPropertiesSize(useCesu8) {
const hasUpdatedProperties = Object.keys(this._updatedProperties).length > 0;
if (!hasUpdatedProperties) {
return 0;
}
const self = this;
const fields = Object.keys(this._updatedProperties).reduce(function (p, c) {
p.push(c, self._properties[c]);
return p;
}, []);
return common.PART_HEADER_LENGTH + util.alignLength(TextList.getByteLength(fields, useCesu8), 8);
};
24 changes: 24 additions & 0 deletions lib/protocol/Connection.js
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,7 @@ Connection.prototype.getAvailableSize = function getAvailableSize(forLobs = fals
if (this._statementContext) {
availableSize -= this._statementContext.size;
}
availableSize -= this._clientInfo.getUpdatedPropertiesSize(this.useCesu8);
return availableSize;
};

Expand Down Expand Up @@ -493,6 +494,10 @@ Connection.prototype.connect = function connect(options, cb) {
{name : common.ClientContextOption.CLIENT_APPLICATION_PROGRAM,
value : this._clientInfo.getApplication()}
]);
this.connectOptions.setOptions([
{name : common.ConnectOption.FLAG_SET1,
value : common.ConnectOptionFlagSet1.SUPPORT_CLIENT_PING}
]);

const compressionFlags = compressor.determineCompressionFlags(options['compress']);
if(compressionFlags) {
Expand Down Expand Up @@ -699,6 +704,25 @@ Connection.prototype._setClientInfo = function _setClientInfo(key, val) {
this._clientInfo.setProperty(key, val);
};

Connection.prototype.isValid = function isValid(options, cb) {
if (!this._queue ||
this.readyState !== 'connected') {
return cb(false);
}
if (this.connectOptions && ((this.connectOptions["flagSet1"] & common.ConnectOptionFlagSet1.SUPPORT_CLIENT_PING) != 0)) {
this.enqueue(request.isValid(options), function (err, reply) {
return cb(err ? false : true);
});
} else {
options = util.extend({
command: "SELECT 1 FROM DUMMY WHERE 1=0"
}, options);
this.executeDirect(options, function (err, reply) {
return cb(err ? false : true);
});
}
};

function ConnectionState() {
this.sessionId = -1;
this.packetCount = -1;
Expand Down
4 changes: 2 additions & 2 deletions lib/protocol/Writer.js
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ Writer.prototype.finializeParameters = function finializeParameters(
// store lob length in header
var length = header.readInt32LE(2);
// readable events might not emit for every chunk so we handle all
// avaliable chunks immediately
// available chunks immediately
while (chunk !== null) {
if (chunk.length > bytesRemainingForLOBs) {
cleanup();
Expand Down Expand Up @@ -334,7 +334,7 @@ Writer.prototype.finalizeWriteLobRequest = function finalizeWriteLobRequest(
// store lob length in header
var length = header.readInt32LE(17);
// readable events might not emit for every chunk so we handle all
// avaliable chunks immediately
// available chunks immediately
while (chunk !== null) {
if (chunk.length > bytesRemaining) {
cleanup();
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 @@ -33,6 +33,7 @@ module.exports = {
OS_USER: 32,
FULL_VERSION_STRING: 44,
COMPRESSION_LEVEL_AND_FLAGS: 49,
FLAG_SET1: 53,
REDIRECTION_TYPE: 57,
REDIRECTED_HOST: 58,
REDIRECTED_PORT: 59,
Expand Down
18 changes: 18 additions & 0 deletions lib/protocol/common/ConnectOptionFlagSet1.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Copyright 2026 SAP AG.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http: //www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
// either express or implied. See the License for the specific
// language governing permissions and limitations under the License.
'use strict';

module.exports = {
SUPPORT_CLIENT_PING: 0x02000000,
};
1 change: 1 addition & 0 deletions lib/protocol/common/ConnectOptionType.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ ConnectOptionType[ConnectOption.IGNORE_UNKNOWN_PARTS] = TypeCode.BOOLEAN;
ConnectOptionType[ConnectOption.DATA_FORMAT_VERSION2] = TypeCode.INT;
ConnectOptionType[ConnectOption.OS_USER] = TypeCode.STRING;
ConnectOptionType[ConnectOption.FULL_VERSION_STRING] = TypeCode.STRING;
ConnectOptionType[ConnectOption.FLAG_SET1] = TypeCode.INT;
ConnectOptionType[ConnectOption.REDIRECTION_TYPE] = TypeCode.INT;
ConnectOptionType[ConnectOption.REDIRECTED_HOST] = TypeCode.STRING;
ConnectOptionType[ConnectOption.REDIRECTED_PORT] = TypeCode.INT;
Expand Down
5 changes: 3 additions & 2 deletions lib/protocol/common/MessageType.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,5 +37,6 @@ module.exports = {
FETCH_NEXT_ITAB: 79,
INSERT_NEXT_ITAB: 80,
BATCH_PREPARE: 81,
DB_CONNECT_INFO: 82
};
DB_CONNECT_INFO: 82,
CLIENT_PING: 94
};
3 changes: 2 additions & 1 deletion lib/protocol/common/PartKind.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,5 +66,6 @@ module.exports = {
BATCH_EXECUTE: 63,
*/
TRANSACTION_FLAGS: 64,
DB_CONNECT_INFO: 67
DB_CONNECT_INFO: 67,
CLIENT_PING: 82
};
1 change: 1 addition & 0 deletions lib/protocol/common/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ exports.CommandOption = require('./CommandOption');
exports.CommitOption = require('./CommitOption');
exports.DbConnectInfoOption = require('./DbConnectInfoOption');
exports.ConnectOption = require('./ConnectOption');
exports.ConnectOptionFlagSet1 = require('./ConnectOptionFlagSet1');
exports.ConnectOptionType = require('./ConnectOptionType');
exports.ClientContextOption = require('./ClientContextOption');
exports.DataFormatVersion = require('./DataFormatVersion');
Expand Down
1 change: 1 addition & 0 deletions lib/protocol/data/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ rw[PartKind.RESULT_SET_METADATA] = ResultSetMetadata;
rw[PartKind.CLIENT_INFO] = TextList;
rw[PartKind.TRANSACTION_FLAGS] = TransactionFlags;
rw[PartKind.DB_CONNECT_INFO] = Options;
rw[PartKind.CLIENT_PING] = Options;

for (var name in PartKind) {
/* jshint forin: false */
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 @@ -144,6 +144,7 @@ ConnectOptions.prototype.KEYS = [
common.ConnectOption.DATA_FORMAT_VERSION2,
common.ConnectOption.OS_USER,
common.ConnectOption.FULL_VERSION_STRING,
common.ConnectOption.FLAG_SET1,
common.ConnectOption.REDIRECTION_TYPE,
common.ConnectOption.REDIRECTED_HOST,
common.ConnectOption.REDIRECTED_PORT,
Expand Down
7 changes: 7 additions & 0 deletions lib/protocol/request/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ exports.writeLob = writeLob;
exports.commit = commit;
exports.rollback = rollback;
exports.dbConnectInfo = dbConnectInfo;
exports.isValid = isValid;

function createSegment(type, options) {
options = options || {};
Expand Down Expand Up @@ -207,3 +208,9 @@ function addCommitOptions(segment, options) {
}
return segment;
}

function isValid(options) {
var segment = createSegment(MessageType.CLIENT_PING, options);
segment.add(PartKind.CLIENT_PING, []);
return segment;
}
Loading