Skip to content
Closed
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

## Unreleased

- Honor the client `useCloudFetch: false` default on the kernel backend so complete results can be fetched without cloud-storage downloads.

- Report untyped NULL columns as STRING in kernel result metadata, matching Thrift. (#470)
- Reject non-scalar query parameter values at bind time with a `ParameterError` on both the Thrift and kernel backends, instead of stringifying them (which could leave a Thrift operation stuck before FINISHED). Newly rejected on the Thrift backend: arrays, plain objects, boxed primitives (e.g. `new String('x')`, `new Number(1)`), functions, symbols, and a `DBSQLParameter` that wraps another `DBSQLParameter`. Primitives, `Date`, `Int64`, and `DBSQLParameter` are still accepted.

Expand Down
7 changes: 7 additions & 0 deletions CONNECTION_PARAMETERS.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,13 @@ backend (the default), the **SEA / Kernel** backend, or both.
| `useKernel` (internal) | `boolean` | — | — | `false` | Selects the backend: `false`/omitted → Thrift, `true` → Kernel. |
| `clientId` (deprecated) | `string` | ✅ | ✅ | — | Runtime-only alias for `userAgentEntry`. `userAgentEntry` wins when both are supplied. |

The `DBSQLClient` constructor configuration accepts `useCloudFetch` (default
`true`) on both backends. Set it to `false` before opening a session to fetch
complete results without cloud-storage downloads. On the kernel backend this
connection setting takes precedence over case-insensitive copies of
`can_cloud_download` in session configuration. Per-statement `useCloudFetch`
overrides remain unsupported on the kernel backend.

## Authentication

| Option | Type | Thrift | Kernel | Default Value | Note |
Expand Down
6 changes: 6 additions & 0 deletions lib/kernel/KernelBackend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,12 @@ export default class KernelBackend implements IBackend {
if (request.configuration !== undefined) {
sessionOptions.sessionConf = { ...request.configuration };
}
if (this.context.getConfig().useCloudFetch === false) {
sessionOptions.sessionConf = Object.fromEntries(
Object.entries(sessionOptions.sessionConf ?? {}).filter(([key]) => key.toLowerCase() !== 'can_cloud_download'),
);
sessionOptions.sessionConf.can_cloud_download = 'false';
}
// Session-level query tags: serialize into the reserved `QUERY_TAGS`
// session conf. The kernel allowlists `QUERY_TAGS` (SESSION_CONF_ALLOWLIST)
// and forwards it onto the SEA `CreateSession` `session_confs`, mirroring
Expand Down
30 changes: 30 additions & 0 deletions tests/unit/kernel/execution.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -735,6 +735,36 @@ describe('KernelBackend', () => {
}
});

[undefined, true, false].forEach((useCloudFetch) => {
it(`openSession() honors useCloudFetch=${useCloudFetch}`, async () => {
const binding = makeBinding(new FakeNativeConnection());
const backend = new KernelBackend({ context: makeContext(undefined, { useCloudFetch }), nativeBinding: binding });
await backend.connect({ host: 'h', path: '/p', token: 't' } as ConnectionOptions);
const configuration = { TIMEZONE: 'UTC' };
await backend.openSession({ configuration });
const conf = (binding.openSessionStub.firstCall.args[0] as { sessionConf?: Record<string, string> }).sessionConf;
expect(conf?.can_cloud_download).to.equal(useCloudFetch === false ? 'false' : undefined);
expect(conf?.TIMEZONE).to.equal('UTC');
expect(configuration).to.deep.equal({ TIMEZONE: 'UTC' });
});
});

['can_cloud_download', 'CAN_CLOUD_DOWNLOAD'].forEach((key) => {
it(`openSession() gives useCloudFetch=false precedence over ${key}`, async () => {
const binding = makeBinding(new FakeNativeConnection());
const backend = new KernelBackend({
context: makeContext(undefined, { useCloudFetch: false }),
nativeBinding: binding,
});
await backend.connect({ host: 'h', path: '/p', token: 't' } as ConnectionOptions);
const configuration = { [key]: 'true', TIMEZONE: 'UTC' };
await backend.openSession({ configuration });
const conf = (binding.openSessionStub.firstCall.args[0] as { sessionConf?: Record<string, string> }).sessionConf;
expect(conf).to.deep.equal({ can_cloud_download: 'false', TIMEZONE: 'UTC' });
expect(configuration).to.deep.equal({ [key]: 'true', TIMEZONE: 'UTC' });
});
});

it('openSession() serializes session-level queryTags into sessionConf.QUERY_TAGS', async () => {
const connection = new FakeNativeConnection();
const binding = makeBinding(connection);
Expand Down
Loading