diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml new file mode 100644 index 0000000..14ad52c --- /dev/null +++ b/.github/workflows/backport.yml @@ -0,0 +1,40 @@ +name: Backport labeled PRs + +# When a PR is merged with a label of the form `backport rel/`, +# 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}`. diff --git a/.github/workflows/integration-test.yml b/.github/workflows/integration-test.yml new file mode 100644 index 0000000..fab183d --- /dev/null +++ b/.github/workflows/integration-test.yml @@ -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 }}." \ No newline at end of file diff --git a/.github/workflows/piper.yaml b/.github/workflows/piper.yaml new file mode 100644 index 0000000..9be165a --- /dev/null +++ b/.github/workflows/piper.yaml @@ -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 diff --git a/cfg/VERSION b/cfg/VERSION index 56c92d5..afd9243 100644 --- a/cfg/VERSION +++ b/cfg/VERSION @@ -1 +1 @@ -2.29.5 +2.29.6 diff --git a/lib/Client.js b/lib/Client.js index 9f9114b..90aaf34 100644 --- a/lib/Client.js +++ b/lib/Client.js @@ -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({ diff --git a/lib/protocol/ClientInfo.js b/lib/protocol/ClientInfo.js index 22630a5..aa2de96 100644 --- a/lib/protocol/ClientInfo.js +++ b/lib/protocol/ClientInfo.js @@ -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; @@ -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); +}; diff --git a/lib/protocol/Connection.js b/lib/protocol/Connection.js index e172586..1cd817c 100644 --- a/lib/protocol/Connection.js +++ b/lib/protocol/Connection.js @@ -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; }; @@ -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) { @@ -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; diff --git a/lib/protocol/Writer.js b/lib/protocol/Writer.js index 1636be5..25b2546 100644 --- a/lib/protocol/Writer.js +++ b/lib/protocol/Writer.js @@ -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(); @@ -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(); diff --git a/lib/protocol/common/ConnectOption.js b/lib/protocol/common/ConnectOption.js index 780b99e..e679b8d 100644 --- a/lib/protocol/common/ConnectOption.js +++ b/lib/protocol/common/ConnectOption.js @@ -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, diff --git a/lib/protocol/common/ConnectOptionFlagSet1.js b/lib/protocol/common/ConnectOptionFlagSet1.js new file mode 100644 index 0000000..8ca6dd1 --- /dev/null +++ b/lib/protocol/common/ConnectOptionFlagSet1.js @@ -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, +}; diff --git a/lib/protocol/common/ConnectOptionType.js b/lib/protocol/common/ConnectOptionType.js index 4b47052..2ebaa8d 100644 --- a/lib/protocol/common/ConnectOptionType.js +++ b/lib/protocol/common/ConnectOptionType.js @@ -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; diff --git a/lib/protocol/common/MessageType.js b/lib/protocol/common/MessageType.js index d9caca3..0e4811d 100644 --- a/lib/protocol/common/MessageType.js +++ b/lib/protocol/common/MessageType.js @@ -37,5 +37,6 @@ module.exports = { FETCH_NEXT_ITAB: 79, INSERT_NEXT_ITAB: 80, BATCH_PREPARE: 81, - DB_CONNECT_INFO: 82 -}; \ No newline at end of file + DB_CONNECT_INFO: 82, + CLIENT_PING: 94 +}; diff --git a/lib/protocol/common/PartKind.js b/lib/protocol/common/PartKind.js index 25f95c7..ae49ff6 100644 --- a/lib/protocol/common/PartKind.js +++ b/lib/protocol/common/PartKind.js @@ -66,5 +66,6 @@ module.exports = { BATCH_EXECUTE: 63, */ TRANSACTION_FLAGS: 64, - DB_CONNECT_INFO: 67 + DB_CONNECT_INFO: 67, + CLIENT_PING: 82 }; diff --git a/lib/protocol/common/index.js b/lib/protocol/common/index.js index 8d2d986..f23df0c 100644 --- a/lib/protocol/common/index.js +++ b/lib/protocol/common/index.js @@ -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'); diff --git a/lib/protocol/data/index.js b/lib/protocol/data/index.js index a3001a6..f356b1d 100644 --- a/lib/protocol/data/index.js +++ b/lib/protocol/data/index.js @@ -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 */ diff --git a/lib/protocol/part/ConnectOptions.js b/lib/protocol/part/ConnectOptions.js index 5045a41..c3c1c2d 100644 --- a/lib/protocol/part/ConnectOptions.js +++ b/lib/protocol/part/ConnectOptions.js @@ -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, diff --git a/lib/protocol/request/index.js b/lib/protocol/request/index.js index aca7e3d..c147c55 100644 --- a/lib/protocol/request/index.js +++ b/lib/protocol/request/index.js @@ -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 || {}; @@ -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; +} diff --git a/package-lock.json b/package-lock.json index 0cb69bb..6a1d352 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "hdb", - "version": "2.29.5", + "version": "2.29.6", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "hdb", - "version": "2.29.5", + "version": "2.29.6", "license": "Apache-2.0", "dependencies": { "iconv-lite": "0.7.0" @@ -248,9 +248,9 @@ "license": "MIT" }, "node_modules/brace-expansion": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz", - "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==", + "version": "1.1.16", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz", + "integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==", "dev": true, "license": "MIT", "dependencies": { @@ -495,9 +495,9 @@ } }, "node_modules/diff": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz", - "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==", + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.4.tgz", + "integrity": "sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==", "dev": true, "license": "BSD-3-Clause", "engines": { @@ -652,9 +652,9 @@ } }, "node_modules/glob/node_modules/brace-expansion": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.1.tgz", - "integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.2.tgz", + "integrity": "sha512-w5JZcKgdhDOgOwm8H+KgbosopHMuGcl6qbulwjtz3SM7I7P3yW1eAjzMPLrIE+NQ9vjgANKHWeMHnrT0OXW1oA==", "dev": true, "license": "MIT", "dependencies": { @@ -883,9 +883,9 @@ } }, "node_modules/js-yaml": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz", - "integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.0.tgz", + "integrity": "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==", "dev": true, "funding": [ { @@ -1062,9 +1062,9 @@ } }, "node_modules/mocha/node_modules/brace-expansion": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.1.tgz", - "integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.2.tgz", + "integrity": "sha512-w5JZcKgdhDOgOwm8H+KgbosopHMuGcl6qbulwjtz3SM7I7P3yW1eAjzMPLrIE+NQ9vjgANKHWeMHnrT0OXW1oA==", "dev": true, "license": "MIT", "dependencies": { @@ -1527,9 +1527,9 @@ } }, "node_modules/test-exclude/node_modules/brace-expansion": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", - "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.7.tgz", + "integrity": "sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==", "dev": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index a9a8c5f..664bc9d 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ ], "name": "hdb", "description": "SAP HANA Database Client for Node", - "version": "2.29.5", + "version": "2.29.6", "repository": { "type": "git", "url": "git://github.com/SAP/node-hdb.git" @@ -53,6 +53,7 @@ }, "overrides": { "safer-buffer": "2.1.2", - "serialize-javascript": "7.0.5" + "serialize-javascript": "7.0.5", + "diff": "8.0.4" } } diff --git a/test/acceptance/db.DataType.js b/test/acceptance/db.DataType.js index d325f69..3e162d2 100644 --- a/test/acceptance/db.DataType.js +++ b/test/acceptance/db.DataType.js @@ -1540,6 +1540,7 @@ describe('db', function () { }); it('should raise input type error', function (done) { + this.timeout(3000); var invalidTestData = [ { value: 5, diff --git a/test/acceptance/db.Lifecycle.js b/test/acceptance/db.Lifecycle.js index 07216aa..49a2414 100644 --- a/test/acceptance/db.Lifecycle.js +++ b/test/acceptance/db.Lifecycle.js @@ -30,6 +30,7 @@ describe('db', function () { }); it('client.readyState is "connected" after successful connect', function (done) { + this.timeout(5000); const client = hdb.createClient(getOptions()); client.connect(function (err) { if (err) return done(err); @@ -42,6 +43,7 @@ describe('db', function () { }); it('client.readyState is "closed" after disconnect', function (done) { + this.timeout(5000); const client = hdb.createClient(getOptions()); client.connect(function (err) { if (err) return done(err); @@ -61,6 +63,7 @@ describe('db', function () { }); it('disconnect when already disconnected is safe', function (done) { + this.timeout(5000); const client = hdb.createClient(getOptions()); client.connect(function (err) { if (err) return done(err); @@ -192,6 +195,7 @@ describe('db', function () { }); it('end() terminates the connection; subsequent exec produces an error', function (done) { + this.timeout(5000); const client = hdb.createClient(getOptions()); client.connect(function (err) { if (err) return done(err); @@ -203,6 +207,35 @@ describe('db', function () { }); }); + it('isValid() returns true when the session is connected, false when the session is disconnected', function (done) { + this.timeout(5000); + const client = hdb.createClient(getOptions()); + client.connect(function (err) { + if (err) return done(err); + client.isValid({}, function (valid) { + valid.should.equal(true); + client.exec("SELECT CURRENT_CONNECTION FROM SYS.DUMMY", function (err, results) { + if (err) return done(err); + const adminClient = hdb.createClient(getOptions()); + adminClient.connect(function (err) { + if (err) return done(err); + adminClient.exec("ALTER SYSTEM DISCONNECT SESSION '" + results[0].CURRENT_CONNECTION + "'", function (err) { + if (err) return done(err); + client.isValid({}, function (valid) { + valid.should.equal(false); + adminClient.disconnect(function (err) { + adminClient.end(); + client.end(); + done(); + }); + }); + }); + }); + }); + }); + }); + }); + }); }); diff --git a/test/acceptance/db.Lob.js b/test/acceptance/db.Lob.js index e55798d..0db6728 100644 --- a/test/acceptance/db.Lob.js +++ b/test/acceptance/db.Lob.js @@ -309,6 +309,34 @@ describe('db', function () { testInsertReadableStream(transformStream, expected, done); }); + it('should insert from a stream when client info is set after prepare', function (done) { + // Regression test for https://github.com/SAP/node-hdb/pull/293 + // Setting client info between prepare and exec caused getAvailableSize to + // over-report available space, leading to Packet size limit exceeded. + const hdb = require('../../'); + const options = require('../db').getOptions({ packetSize: 65536 }); + const smallClient = hdb.createClient(options); + smallClient.connect(function (err) { + if (err) { return done(err); } + smallClient.exec('CREATE LOCAL TEMPORARY TABLE #lob_test (data BLOB)', function (err) { + if (err) { return done(err); } + smallClient.prepare('INSERT INTO #lob_test VALUES (?)', function (err, stmt) { + if (err) { return done(err); } + smallClient.setClientInfo('size', '64'); + const gen = async function* () { + for (let i = 0; i < 64; i++) yield Buffer.alloc(1024); + }; + stmt.exec([stream.Readable.from(gen(), { objectMode: false })], function (err) { + smallClient.exec('DROP TABLE #lob_test', function () { + smallClient.end(); + done(err); + }); + }); + }); + }); + }); + }); + }); }); diff --git a/test/lib.Connection.js b/test/lib.Connection.js index acd74a4..6ec8ecf 100644 --- a/test/lib.Connection.js +++ b/test/lib.Connection.js @@ -413,6 +413,17 @@ describe('Lib', function () { var c8 = createConnection({ packetSize: ps, packetSizeLimit: psl }); c8.getAvailableSize(false).should.equal(packetSizeMax - totalHeaderLength); c8.getAvailableSize(true).should.equal(packetSizeMax - totalHeaderLength); + + // client info property set: LOCALE(6 bytes) + en_US(5 bytes), each +1 indicator = 13 data bytes, + // aligned to 8 = 16, plus PART_HEADER_LENGTH(16) = 32 total + var c9 = createConnection(); + c9._statementContext = { + size: 32, + }; + c9.getClientInfo().setProperty('LOCALE', 'en_US'); + var clientInfoPartSize = 32; + c9.getAvailableSize().should.equal(packetSizeDefault - totalHeaderLength - 32 - clientInfoPartSize); + c9.getAvailableSize(true).should.equal(packetSizeDefault - totalHeaderLength - 32 - clientInfoPartSize); }); it('should parse a reply', function () { diff --git a/test/lib.Stringifier.js b/test/lib.Stringifier.js index 132eab3..e0b02e7 100644 --- a/test/lib.Stringifier.js +++ b/test/lib.Stringifier.js @@ -68,16 +68,16 @@ describe('Lib', function () { function testStringifier(chunks, rows, done) { /* jshint validthis:true */ - var data = ''; - var stringifier = this || lib.createJSONStringifier(); + let data = ''; + const stringifier = this || lib.createJSONStringifier(); stringifier.on('error', function (err) { done(err); }).on('readable', function () { - var chunk = this.read(); - if (chunk) { + let chunk; + while ((chunk = this.read()) !== null) { data += chunk; } - }).on('finish', function () { + }).on('end', function () { JSON.parse(data).should.eql(rows); done(); }); diff --git a/test/mocha.opts b/test/mocha.opts deleted file mode 100644 index 64066d6..0000000 --- a/test/mocha.opts +++ /dev/null @@ -1,2 +0,0 @@ ---require should ---growl \ No newline at end of file