From 37aa43da4967d59a2dc7c4a75095cfb0f3b22c7d Mon Sep 17 00:00:00 2001 From: Serge Klochkov <3175289+slvrtrn@users.noreply.github.com> Date: Thu, 6 Jun 2024 21:11:47 +0200 Subject: [PATCH] Allow to override session id for a particular request (#281) Co-authored-by: Hollis Wu --- CHANGELOG.md | 1 + jasmine.node.integration.json | 7 +- .../__tests__/integration/exec.test.ts | 22 ------ .../__tests__/integration/session.test.ts | 77 +++++++++++++++++++ packages/client-common/src/client.ts | 10 ++- packages/client-common/src/config.ts | 2 +- 6 files changed, 91 insertions(+), 28 deletions(-) create mode 100644 packages/client-common/__tests__/integration/session.test.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 1ae39301..7b6c3df4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## New features - Added an option to override the credentials for a particular `query`/`command`/`exec`/`insert` request via the `BaseQueryParams.auth` setting; when set, the credentials will be taken from there instead of the username/password provided during the client instantiation. +- Allow overriding `session_id` per query ([@holi0317](https://github.com/Holi0317), [#271](https://github.com/ClickHouse/clickhouse-js/issues/271)). # 1.0.2 (Common, Node.js, Web) diff --git a/jasmine.node.integration.json b/jasmine.node.integration.json index 4122efd1..f1b59162 100644 --- a/jasmine.node.integration.json +++ b/jasmine.node.integration.json @@ -1,6 +1,9 @@ { - "spec_dir": "packages/client-node/__tests__", - "spec_files": ["integration/*.test.ts"], + "spec_dir": "packages/", + "spec_files": [ + "client-node/__tests__/integration/*.test.ts", + "client-common/__tests__/integration/*.test.ts" + ], "env": { "failSpecWithNoExpectations": true, "stopSpecOnExpectationFailure": true, diff --git a/packages/client-common/__tests__/integration/exec.test.ts b/packages/client-common/__tests__/integration/exec.test.ts index e32848ea..9f87adf8 100644 --- a/packages/client-common/__tests__/integration/exec.test.ts +++ b/packages/client-common/__tests__/integration/exec.test.ts @@ -72,28 +72,6 @@ describe('exec', () => { ) }) - describe('sessions', () => { - let sessionClient: ClickHouseClient - beforeEach(() => { - sessionClient = createTestClient({ - session_id: `test-session-${guid()}`, - }) - }) - afterEach(async () => { - await sessionClient.close() - }) - - it('should allow the use of a session', async () => { - // Temporary tables cannot be used without a session - const tableName = `temp_table_${guid()}` - await expectAsync( - sessionClient.exec({ - query: `CREATE TEMPORARY TABLE ${tableName} (val Int32)`, - }), - ).toBeResolved() - }) - }) - it('can specify a parameterized query', async () => { const result = await client.query({ query: `SELECT * from system.tables where name = 'numbers'`, diff --git a/packages/client-common/__tests__/integration/session.test.ts b/packages/client-common/__tests__/integration/session.test.ts new file mode 100644 index 00000000..a7e26db2 --- /dev/null +++ b/packages/client-common/__tests__/integration/session.test.ts @@ -0,0 +1,77 @@ +import type { ClickHouseClient } from '@clickhouse/client-common' +import { createTestClient, guid, TestEnv, whenOnEnv } from '@test/utils' + +describe('sessions settings', () => { + let client: ClickHouseClient + afterEach(async () => { + await client.close() + }) + + whenOnEnv(TestEnv.LocalSingleNode).it('should use sessions', async () => { + client = createTestClient({ + session_id: `test-session-${guid()}`, + }) + + const tableName = `temp_table_${guid()}` + await client.command({ + query: getTempTableDDL(tableName), + }) + await client.insert({ + table: tableName, + values: [{ id: 42, name: 'foo' }], + format: 'JSONEachRow', + }) + await client.exec({ + query: `INSERT INTO ${tableName} VALUES (43, 'bar')`, + }) + const rs = await client.query({ + query: `SELECT * FROM ${tableName} ORDER BY id ASC`, + format: 'JSONEachRow', + }) + expect(await rs.json()).toEqual([ + { id: 42, name: 'foo' }, + { id: 43, name: 'bar' }, + ]) + }) + + whenOnEnv(TestEnv.LocalSingleNode).it( + 'should use session override', + async () => { + // no session_id by default + client = createTestClient() + + const sessionId = `test-session-${guid()}` + const tableName = `temp_table_${guid()}` + await client.command({ + query: getTempTableDDL(tableName), + session_id: sessionId, + }) + await client.insert({ + table: tableName, + values: [{ id: 144, name: 'qaz' }], + format: 'JSONEachRow', + session_id: sessionId, + }) + await client.exec({ + query: `INSERT INTO ${tableName} VALUES (255, 'qux')`, + session_id: sessionId, + }) + const rs = await client.query({ + query: `SELECT * FROM ${tableName} ORDER BY id ASC`, + format: 'JSONEachRow', + session_id: sessionId, + }) + expect(await rs.json()).toEqual([ + { id: 144, name: 'qaz' }, + { id: 255, name: 'qux' }, + ]) + }, + ) + + function getTempTableDDL(tableName: string) { + return ` + CREATE TEMPORARY TABLE ${tableName} + (id Int32, name String) + ` + } +}) diff --git a/packages/client-common/src/client.ts b/packages/client-common/src/client.ts index 8cb7aaff..cd4e531b 100644 --- a/packages/client-common/src/client.ts +++ b/packages/client-common/src/client.ts @@ -26,6 +26,9 @@ export interface BaseQueryParams { /** A specific `query_id` that will be sent with this request. * If it is not set, a random identifier will be generated automatically by the client. */ query_id?: string + /** A specific ClickHouse Session id for this query. + * If it is not set, {@link BaseClickHouseClientConfigOptions.session_id} will be used. + * @default undefined (no override) */ session_id?: string /** When defined, overrides the credentials from the {@link BaseClickHouseClientConfigOptions.username} * and {@link BaseClickHouseClientConfigOptions.password} settings for this particular request. @@ -163,9 +166,10 @@ export class ClickHouseClient { ): Promise> { const format = params.format ?? 'JSON' const query = formatQuery(params.query, format) + const queryParams = this.withClientQueryParams(params) const { stream, query_id } = await this.connection.query({ query, - ...this.withClientQueryParams(params), + ...queryParams, }) return this.makeResultSet(stream, format, query_id, (err) => { this.logWriter.error({ @@ -173,7 +177,7 @@ export class ClickHouseClient { module: 'Client', message: 'Error while processing the ResultSet.', args: { - session_id: this.sessionId, + session_id: queryParams.session_id, query, query_id, }, @@ -256,10 +260,10 @@ export class ClickHouseClient { ...this.clientClickHouseSettings, ...params.clickhouse_settings, }, - session_id: this.sessionId, query_params: params.query_params, abort_signal: params.abort_signal, query_id: params.query_id, + session_id: params.session_id ?? this.sessionId, auth: params.auth, } } diff --git a/packages/client-common/src/config.ts b/packages/client-common/src/config.ts index fa3ba9f7..a24b6996 100644 --- a/packages/client-common/src/config.ts +++ b/packages/client-common/src/config.ts @@ -61,7 +61,7 @@ export interface BaseClickHouseClientConfigOptions { level?: ClickHouseLogLevel } /** ClickHouse Session id to attach to the outgoing requests. - * @default empty string */ + * @default empty string (no session) */ session_id?: string /** @deprecated since version 1.0.0. Use {@link http_headers} instead.
* Additional HTTP headers to attach to the outgoing requests.