Skip to content
Draft
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
13 changes: 1 addition & 12 deletions lib/kernel/KernelSessionBackend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -393,19 +393,8 @@ export default class KernelSessionBackend implements ISessionBackend {

public async getPrimaryKeys(request: PrimaryKeysRequest): Promise<IOperationBackend> {
this.failIfClosed();
// The kernel requires a catalog for primary-key lookup (`Identifier::new`
// rejects an empty string). The Thrift backend can forward an undefined
// catalog and let the server resolve a default; the kernel path cannot,
// so reject up front with a clear, actionable message rather than passing
// `''` and surfacing the kernel's opaque "identifier must not be empty".
if (request.catalogName === undefined || request.catalogName === '') {
throw new HiveDriverError(
'kernel getPrimaryKeys requires a catalog — pass `catalogName` explicitly. (The Thrift backend ' +
'can omit it and let the server resolve a default; the kernel kernel path requires it.)',
);
}
return this.runMetadata(() =>
this.connection.getPrimaryKeys(request.catalogName as string, request.schemaName, request.tableName),
this.connection.getPrimaryKeys(request.catalogName, request.schemaName, request.tableName),
);
}

Expand Down
2 changes: 1 addition & 1 deletion native/kernel/index.d.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 20 additions & 0 deletions tests/unit/DBSQLSession.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import DBSQLSession, { numberToInt64 } from '../../lib/DBSQLSession';
import InfoValue from '../../lib/dto/InfoValue';
import Status from '../../lib/dto/Status';
import DBSQLOperation from '../../lib/DBSQLOperation';
import { PrimaryKeysRequest } from '../../lib/contracts/IDBSQLSession';
import ISessionBackend from '../../lib/contracts/ISessionBackend';
import ParameterError from '../../lib/errors/ParameterError';
import { TSessionHandle, TProtocolVersion } from '../../thrift/TCLIService_types';
Expand Down Expand Up @@ -514,6 +515,25 @@ describe('DBSQLSession', () => {
});

describe('getPrimaryKeys', () => {
it('should forward null, omitted, and empty namespace names unchanged for Thrift', async () => {
for (const catalogName of [undefined, null, '', 'catalog']) {
for (const schemaName of [undefined, null, '', 'schema']) {
const context = new ClientContextStub();
const session = createSessionForTest({ handle: sessionHandleStub, context });
// eslint-disable-next-line no-await-in-loop
const result = await session.getPrimaryKeys({
catalogName,
schemaName,
tableName: 't1',
} as PrimaryKeysRequest);
expect(result).instanceOf(DBSQLOperation);
expect(context.driver.getPrimaryKeysReq?.catalogName).to.equal(catalogName);
expect(context.driver.getPrimaryKeysReq?.schemaName).to.equal(schemaName);
expect(context.driver.getPrimaryKeysReq?.tableName).to.equal('t1');
}
}
});

it('should run operation', async () => {
const session = createSessionForTest({ handle: sessionHandleStub, context: new ClientContextStub() });
const result = await session.getPrimaryKeys({
Expand Down
27 changes: 13 additions & 14 deletions tests/unit/kernel/execution.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import KernelSessionBackend from '../../../lib/kernel/KernelSessionBackend';
import KernelOperationBackend from '../../../lib/kernel/KernelOperationBackend';
import { KernelNativeBinding, KernelConnection, KernelStatement } from '../../../lib/kernel/KernelNativeLoader';
import IClientContext, { ClientConfig } from '../../../lib/contracts/IClientContext';
import { PrimaryKeysRequest } from '../../../lib/contracts/IDBSQLSession';
import IDBSQLLogger, { LogLevel } from '../../../lib/contracts/IDBSQLLogger';
import HiveDriverError from '../../../lib/errors/HiveDriverError';
import ParameterError from '../../../lib/errors/ParameterError';
Expand Down Expand Up @@ -1094,25 +1095,23 @@ describe('KernelSessionBackend', () => {
]);
});

it('getPrimaryKeys rejects an omitted catalog up front (the kernel requires one)', async () => {
it('getPrimaryKeys forwards null, omitted, and empty namespace names unchanged', async () => {
const connection = new FakeNativeConnection();
const session = makeSession(connection);
for (const request of [
{ schemaName: 'def', tableName: 't' },
{ catalogName: '', schemaName: 'def', tableName: 't' },
]) {
let thrown: unknown;
try {
const expected: unknown[][] = [];
for (const catalogName of [undefined, null, '', 'main']) {
for (const schemaName of [undefined, null, '', 'def']) {
// eslint-disable-next-line no-await-in-loop
await session.getPrimaryKeys(request);
} catch (err) {
thrown = err;
const operation = await session.getPrimaryKeys({
catalogName,
schemaName,
tableName: 't',
} as PrimaryKeysRequest);
expect(operation).to.be.instanceOf(KernelOperationBackend);
expected.push(['getPrimaryKeys', catalogName, schemaName, 't']);
}
expect(thrown, `expected reject for ${JSON.stringify(request)}`).to.be.instanceOf(HiveDriverError);
expect((thrown as Error).message).to.match(/requires a catalog/);
}
// The kernel call must NOT be reached (no empty-identifier sent over FFI).
expect(connection.metadataCalls.filter((c) => c[0] === 'getPrimaryKeys')).to.have.length(0);
expect(connection.metadataCalls).to.deep.equal(expected);
});

it('getInfo synthesizes the three server-answered info types and rejects the rest', async () => {
Expand Down
Loading