Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
cda34ab
feat(kernel): complete SQL volume file transfers
aakash-saravanan-db Nov 11, 2026
489d6c5
style: format volume transfer documentation
aakash-saravanan-db Nov 11, 2026
8ad809e
fix(volume): normalize changelog and pin validated native source
aakash-saravanan-db Nov 11, 2026
2148a89
chore(volume): pin native file-mode fix
aakash-saravanan-db Nov 11, 2026
e75455b
fix(kernel): align native pin and brace transfer error branch
aakash-saravanan-db Nov 11, 2026
9c1b37b
build(kernel): pin reviewed Volume transfer implementation
aakash-saravanan-db Nov 11, 2026
417496a
docs(kernel): clarify file transfer options and source dependency
aakash-saravanan-db Nov 11, 2026
99885c1
build(kernel): pin reviewed file transfer source
aakash-saravanan-db Nov 12, 2026
c2d8000
build(kernel): pin reviewed file transfer source
aakash-saravanan-db Nov 12, 2026
37de7fd
fix(volume): preserve staging errors for local permissions
aakash-saravanan-db Nov 12, 2026
b021a9d
build(kernel): pin reviewed file transfer source
aakash-saravanan-db Nov 12, 2026
76e1c43
build(kernel): pin reviewed file transfer source
aakash-saravanan-db Nov 12, 2026
141209e
build(kernel): pin reviewed file transfer source
aakash-saravanan-db Nov 12, 2026
caa35d2
build(kernel): pin reviewed file transfer source
aakash-saravanan-db Nov 12, 2026
c024398
build(kernel): pin reviewed file transfer source
aakash-saravanan-db Nov 12, 2026
47fc11d
build(kernel): pin reviewed file transfer source
aakash-saravanan-db Nov 12, 2026
6b8a321
build(kernel): pin reviewed file transfer source
aakash-saravanan-db Nov 12, 2026
07ac457
build(kernel): pin reviewed file transfer source
aakash-saravanan-db Nov 12, 2026
9ab5549
build(kernel): pin reviewed file transfer source
aakash-saravanan-db Nov 12, 2026
89abede
build(kernel): pin reviewed file transfer source
aakash-saravanan-db Nov 12, 2026
be17f4f
build(kernel): pin reviewed file transfer source
aakash-saravanan-db Nov 12, 2026
c870f3e
build(kernel): pin reviewed file transfer source
aakash-saravanan-db Nov 12, 2026
8bf93ff
build(kernel): pin reviewed file transfer source
aakash-saravanan-db Nov 12, 2026
2ca4062
build(kernel): pin reviewed file transfer source
aakash-saravanan-db Nov 12, 2026
c72ec90
build(kernel): pin reviewed file transfer source
aakash-saravanan-db Nov 12, 2026
5a608f8
fix(kernel): preserve file transfer operation states
aakash-saravanan-db Nov 12, 2026
e60eaf3
build(kernel): pin reviewed file transfer source
aakash-saravanan-db Nov 12, 2026
d9081aa
test: cover transfer errors before auth and argument errors
aakash-saravanan-db Nov 12, 2026
916a9d3
build(kernel): pin reviewed file transfer source
aakash-saravanan-db Nov 12, 2026
17f71ae
Explain the specific kernel transfer error code
aakash-saravanan-db Nov 12, 2026
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## Unreleased
Comment thread
peco-review-bot[bot] marked this conversation as resolved.
Resolved

- Honor `stagingAllowedLocalPath` for SQL PUT, GET, and REMOVE through the native backend and report transfer errors as `StagingError`. Requires matching native packages. (#582)
- CloudFetch retry-exhaustion errors now identify the download and the last failure: the `RetryError` message reads `CloudFetch result download failed: <original message> (last error: <cause>)`. `<cause>` is `HTTP <status>: <statusText>` (or `HTTP <status>` when the status text is empty) for HTTP failures, and `network error <code>`, `network error (<type>)` (e.g. `aborted`, when there is no `code`) or a bare `network error` for network failures (the raw network-error message, which can contain the presigned URL, is not copied). If no cause can be determined, the ` (last error: …)` suffix is omitted. The `RetryError` instance, `errorCode` and `payload` (including the node-fetch `Request` / `Response`) are unchanged. (#471)
- 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
26 changes: 13 additions & 13 deletions CONNECTION_PARAMETERS.md
Original file line number Diff line number Diff line change
Expand Up @@ -156,19 +156,19 @@ telemetry implementation, so only the options explicitly forwarded by

## Per-statement options (`session.executeStatement(sql, options)`)

| Option | Type | Thrift | Kernel | Default Value | Note |
| ------------------------- | ------------------------------------------------------- | :----: | :----: | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `queryTimeout` | `number \| bigint \| Int64` | ✅ | ❌ | — | Thrift sends this server-side timeout for Compute clusters. Kernel ignores it; SQL Warehouses use `STATEMENT_TIMEOUT`. |
| `runAsync` | `boolean` | ❌ | ✅ | — / `false` | Thrift always sends asynchronously and ignores the option. Kernel `false`/omitted uses direct execution; `true` submits and polls. This execute option is not deprecated. |
| `maxRows` | `number \| bigint \| Int64 \| null` | ✅ | ❌ | `100000` / — | Thrift uses it for the initial Direct Results row count; `null` disables Direct Results. Kernel does not retain or forward the supplied value. |
| `useCloudFetch` | `boolean` | ✅ | ❌ | `true` / — | Kernel logs and ignores this per-statement hint; Kernel result fetching owns its CloudFetch behavior. |
| `useLZ4Compression` | `boolean` | ✅ | ❌ | `true` / — | Thrift uses it when supported and when the result is not CloudFetch. Kernel owns and auto-detects result compression. |
| `stagingAllowedLocalPath` | `string \| string[]` | ✅ | ❌ | — | Local allowlist for Thrift volume/staging operations. Kernel volume operations are unsupported. |
| `namedParameters` | `Record<string, DBSQLParameter \| DBSQLParameterValue>` | ✅ | ✅ | — | Named SQL parameters. Cannot be combined with non-empty `ordinalParameters`. Thrift requires a protocol that supports parameterized queries. |
| `ordinalParameters` | `Array<DBSQLParameter \| DBSQLParameterValue>` | ✅ | ✅ | — | Positional SQL parameters. Cannot be combined with non-empty `namedParameters`. |
| `queryTags` | `Record<string, string \| null \| undefined>` | ✅ | ✅ | — | Serialized into the per-statement `query_tags` conf overlay on both. |
| `rowLimit` | `number` | ❌ | ✅ | — | Kernel-only server-side row cap. Thrift logs and ignores it. |
| `statementConf` | `Record<string, string>` | ❌ | ✅ | — | Kernel-only per-statement Spark conf overlay. Structured `queryTags` overwrite its `query_tags` key when non-empty. |
| Option | Type | Thrift | Kernel | Default Value | Note |
| ------------------------- | ------------------------------------------------------- | :----: | :----: | ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `queryTimeout` | `number \| bigint \| Int64` | ✅ | ❌ | — | Thrift sends this server-side timeout for Compute clusters. Kernel ignores it; SQL Warehouses use `STATEMENT_TIMEOUT`. |
| `runAsync` | `boolean` | ❌ | ✅ | — / `false` | Thrift always sends asynchronously and ignores the option. Kernel `false`/omitted uses direct execution; `true` submits and polls. This execute option is not deprecated. |
| `maxRows` | `number \| bigint \| Int64 \| null` | ✅ | ❌ | `100000` / — | Thrift uses it for the initial Direct Results row count; `null` disables Direct Results. Kernel does not retain or forward the supplied value. |
| `useCloudFetch` | `boolean` | ✅ | ❌ | `true` / — | Kernel logs and ignores this per-statement hint; Kernel result fetching owns its CloudFetch behavior. |
| `useLZ4Compression` | `boolean` | ✅ | ❌ | `true` / — | Thrift uses it when supported and when the result is not CloudFetch. Kernel owns and auto-detects result compression. |
| `stagingAllowedLocalPath` | `string \| string[]` | ✅ | ✅ | — | Local allowlist for SQL PUT/GET; also enables SQL REMOVE without a local path. Native transfers require the kernel revision in `KERNEL_REV` and matching native packages; leaving the option unset returns transfer instructions. |
| `namedParameters` | `Record<string, DBSQLParameter \| DBSQLParameterValue>` | ✅ | ✅ | — | Named SQL parameters. Cannot be combined with non-empty `ordinalParameters`. Thrift requires a protocol that supports parameterized queries. |
| `ordinalParameters` | `Array<DBSQLParameter \| DBSQLParameterValue>` | ✅ | ✅ | — | Positional SQL parameters. Cannot be combined with non-empty `namedParameters`. |
| `queryTags` | `Record<string, string \| null \| undefined>` | ✅ | ✅ | — | Serialized into the per-statement `query_tags` conf overlay on both. |
| `rowLimit` | `number` | ❌ | ✅ | — | Kernel-only server-side row cap. Thrift logs and ignores it. |
| `statementConf` | `Record<string, string>` | ❌ | ✅ | — | Kernel-only per-statement Spark conf overlay. Structured `queryTags` overwrite its `query_tags` key when non-empty. |

## Metadata-operation request parameters

Expand Down
2 changes: 1 addition & 1 deletion KERNEL_REV
Original file line number Diff line number Diff line change
@@ -1 +1 @@
6f4b6d607a61bdde61dcda2cf82bc8e80fd32cb9
37c7a3ec1b54f4690d9325d65f2220e0bb64b83e
112 changes: 65 additions & 47 deletions lib/kernel/KernelErrorMapping.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import HiveDriverError from '../errors/HiveDriverError';
import AuthenticationError from '../errors/AuthenticationError';
import OperationStateError, { OperationStateErrorCode } from '../errors/OperationStateError';
import ParameterError from '../errors/ParameterError';
import StagingError from '../errors/StagingError';

/**
* Sentinel prefix the napi binding's `napi_err_from_kernel` puts on
Expand All @@ -27,6 +28,8 @@ export interface KernelErrorShape {
message: string;
/** Optional SQLSTATE — five-char alphanumeric, when the kernel was able to surface it. */
sqlstate?: string;
/** Distinguishes transfer failures from broader kernel error categories. */
errorCode?: string;
}

/**
Expand Down Expand Up @@ -125,6 +128,9 @@ function defineErrorMetadata<K extends string, V>(error: Error, key: K, value: V
* DataLoss, Internal,
* InvalidStatementHandle, SqlError → HiveDriverError
*
* File-transfer diagnostics map to StagingError except Cancelled and Timeout,
* which retain their operation-state errors for lifecycle handling.
*
* Unknown `code` values (e.g. if the kernel adds a new variant) fall through
* to HiveDriverError so the driver never silently drops an error. The kernel's
* `ErrorCode` is `#[non_exhaustive]` so this can legitimately happen.
Expand All @@ -137,58 +143,65 @@ export function mapKernelErrorToJsError(kErr: KernelErrorShape): ErrorWithSqlSta

let error: ErrorWithSqlState;

switch (code as KernelErrorCode) {
case 'Unauthenticated':
case 'PermissionDenied':
error = new AuthenticationError(message);
break;
if (
code !== 'Cancelled' &&
code !== 'Timeout' &&
(kErr.errorCode === 'FILE_TRANSFER_ERROR' || kErr.errorCode === 'FILE_TRANSFER_PERMISSION_ERROR')
) {
error = new StagingError(message);
} else {
switch (code as KernelErrorCode) {
case 'Unauthenticated':
case 'PermissionDenied':
error = new AuthenticationError(message);
break;

case 'Cancelled':
// OperationStateError with the Canceled code carries the kernel message
// through the response.displayMessage fallback path.
error = new OperationStateError(OperationStateErrorCode.Canceled);
error.message = message;
break;
case 'Cancelled':
// OperationStateError with the Canceled code carries the kernel message
// through the response.displayMessage fallback path.
error = new OperationStateError(OperationStateErrorCode.Canceled);
error.message = message;
break;

case 'Timeout':
error = new OperationStateError(OperationStateErrorCode.Timeout);
error.message = message;
break;
case 'Timeout':
error = new OperationStateError(OperationStateErrorCode.Timeout);
error.message = message;
break;

case 'InvalidArgument':
error = new ParameterError(message);
break;
case 'InvalidArgument':
error = new ParameterError(message);
break;

case 'SqlError': {
// A server-reported SQL execution failure (kernel `SqlError`, e.g. a
// bad query, missing table, divide-by-zero, invalid cast). The Thrift
// backend surfaces the same situation as `OperationStateError(Error)`
// when the operation reaches ERROR_STATE (see DBSQLOperation), so map
// SqlError to the same class for backend parity. OperationStateError
// extends HiveDriverError, so existing `instanceof HiveDriverError`
// catches are unaffected.
const stateError = new OperationStateError(OperationStateErrorCode.Error);
stateError.message = message;
error = stateError;
break;
}
case 'SqlError': {
// A server-reported SQL execution failure (kernel `SqlError`, e.g. a
// bad query, missing table, divide-by-zero, invalid cast). The Thrift
// backend surfaces the same situation as `OperationStateError(Error)`
// when the operation reaches ERROR_STATE (see DBSQLOperation), so map
// SqlError to the same class for backend parity. OperationStateError
// extends HiveDriverError, so existing `instanceof HiveDriverError`
// catches are unaffected.
const stateError = new OperationStateError(OperationStateErrorCode.Error);
stateError.message = message;
error = stateError;
break;
}

// All remaining kernel ErrorCode variants map to the base driver error class.
// M0 intentionally does not introduce new error classes; M1 may add nuance.
case 'NotFound':
case 'ResourceExhausted':
case 'Unavailable':
case 'DataLoss':
case 'Internal':
case 'InvalidStatementHandle':
case 'NetworkError':
error = new HiveDriverError(message);
break;
// All remaining kernel ErrorCode variants map to the base driver error class.
case 'NotFound':
case 'ResourceExhausted':
case 'Unavailable':
case 'DataLoss':
case 'Internal':
case 'InvalidStatementHandle':
case 'NetworkError':
error = new HiveDriverError(message);
break;

default:
// Unknown/future kernel variant — never drop the error, surface as base class.
error = new HiveDriverError(message);
break;
default:
// Unknown/future kernel variant — never drop the error, surface as base class.
error = new HiveDriverError(message);
break;
}
}

if (sqlstate !== undefined) {
Expand Down Expand Up @@ -305,7 +318,12 @@ export function decodeNapiKernelError(err: unknown): Error {
const msg = envelope.message as string;
const sqlState = typeof envelope.sqlState === 'string' ? envelope.sqlState : undefined;

const jsErr = mapKernelErrorToJsError({ code, message: msg, sqlstate: sqlState });
const jsErr = mapKernelErrorToJsError({
code,
message: msg,
sqlstate: sqlState,
errorCode: typeof envelope.errorCode === 'string' ? envelope.errorCode : undefined,
});

const meta = buildKernelMetadata(envelope);
// Skip the namespace attachment entirely when no fields validated
Expand Down
23 changes: 8 additions & 15 deletions lib/kernel/KernelSessionBackend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,10 +148,8 @@ export default class KernelSessionBackend implements ISessionBackend {
* `query_tags` key (the same wire shape Thrift's `serializeQueryTags`
* produces), merged with any explicit `statementConf`.
*
* Still rejected (genuinely unsupported on kernel, rather than silently
* dropped): `useCloudFetch` (governed by the kernel `ResultConfig`, not a
* per-statement knob), `useLZ4Compression` (kernel owns result compression),
* and `stagingAllowedLocalPath` (volume operations). `maxRows` is applied by
* `stagingAllowedLocalPath` configures kernel file transfers. `useCloudFetch`
* and `useLZ4Compression` remain kernel-owned settings. `maxRows` is applied by
* the facade at fetch time, so it is intentionally not handled here.
*/
public async executeStatement(statement: string, options: ExecuteStatementOptions): Promise<IOperationBackend> {
Expand All @@ -160,8 +158,7 @@ export default class KernelSessionBackend implements ISessionBackend {
// Per-statement options the kernel backend doesn't honour are treated as
// NO-OPs (logged at warn), not errors — so call sites written for the Thrift
// backend are drop-in on the kernel path. These are perf/format hints the
// kernel governs internally (useCloudFetch / useLZ4Compression) or a feature
// it doesn't expose yet (staging); ignoring them cannot change query results.
// kernel governs internally (useCloudFetch / useLZ4Compression) and do not change query results.
// NB: parameter binding (compound/BINARY) is deliberately NOT no-op'd — a
// dropped param would silently change results, so it still throws.
if (options.useCloudFetch !== undefined) {
Expand All @@ -180,15 +177,6 @@ export default class KernelSessionBackend implements ISessionBackend {
'kernel executeStatement: ignoring per-statement `useLZ4Compression` — result compression is governed by the kernel (no-op on kernel).',
);
}
if (options.stagingAllowedLocalPath !== undefined) {
this.context
.getLogger()
.log(
LogLevel.warn,
'kernel executeStatement: ignoring `stagingAllowedLocalPath` — volume/staging operations are not yet supported on the kernel backend (no-op).',
);
}

// `runAsync` selects the kernel execution path. NOTE: this is a kernel-
// specific use of the option — the Thrift backend hardcodes `runAsync: true`
// on the wire and never reads `options.runAsync`, so the field is a no-op
Expand Down Expand Up @@ -299,6 +287,11 @@ export default class KernelSessionBackend implements ISessionBackend {
}

const execOptions: KernelNativeExecuteOptions = {};
if (options.stagingAllowedLocalPath !== undefined) {
Comment thread
peco-review-bot[bot] marked this conversation as resolved.
Resolved

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium — This PR deletes the stagingAllowedLocalPath warning and starts sending stagingAllowedLocalPaths to the native binding. But package.json still installs the 1.4.0 native packages, which were built before this field existed. napi-rs ignores object fields it doesn't recognise, so on the binding users actually get, the option is dropped without any message.

The result is worse than before. A PUT/GET with stagingAllowedLocalPath set returns the instruction row and moves no file. The kernel metadata hard-codes isStagingOperation: false (KernelOperationBackend.ts:366), so the Thrift-side handler in DBSQLSession.executeStatement doesn't step in either. The caller gets a successful operation, nothing is logged, and the upload never happened. Before this PR, the same call at least logged a warning that staging was unsupported.

The description says publishing matching native packages is a coordinated follow-up (kernel#443). Until then, this commit on its own is a silent regression, and the Unreleased CHANGELOG entry advertises a feature the pinned binding can't deliver. Options: bump the native pins in the same change, keep the warning until the pins move, or gate on a binding capability or version check.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed that the published package dependency must move before this connector merges or releases. The Dependencies section explicitly gates this PR on publication of matching native packages, a package.json/lockfile bump and installed-package acceptance under kernel issue #443. No version has been assigned, so I have not invented a version or treated source-built validation as published-package support. The current KERNEL_REV pins the validated source for source-build CI; the released 1.4.0 package does not satisfy this gate.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. Gating the merge on the published native packages and the package.json/lockfile bump does address the concern, and not guessing a version number makes sense. The head code still has the warning removed and still sends stagingAllowedLocalPaths while the 1.4.0 pins are in place. So the silent no-op is still in the code, and I'll leave this thread open as the tracker for that gate. It can be resolved once the pins move to a binding that supports the field, or if the warning (or a capability check) is restored in the meantime. One small suggestion until then: mark the CHANGELOG Unreleased entry as depending on kernel#443, so nobody cuts a release from this branch by mistake.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium — This PR removes the warn-level "ignoring stagingAllowedLocalPath" log and now sends stagingAllowedLocalPaths to the binding. But package.json still pins the native packages to 1.4.0, which predate databricks-sql-kernel#439. napi-rs ignores unknown object fields, so with the native packages that actually ship, the option is dropped with no error. Because the kernel backend always reports isStagingOperation: false, DBSQLSession.executeStatement does not run its own transfer either. The result: a kernel-backend PUT/GET/REMOVE returns the instruction row and moves no file, and nothing in the logs says why. Before this change, users at least got a warning.

The PR description says a dependency update is needed before release, but until that lands, main silently drops the option. Two ways to close the gap: land this together with the native-package bump, or have the backend detect binding support (for example from the binding version or a capability flag) and keep the warning, or throw, when the binding is too old.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirmed; this is an unresolved pre-merge dependency gate. Kernel #443 tracks publishing the new native packages and updating package.json/package-lock.json in this PR. Published 1.4.0 lacks the new option. Locally rebuilt source-pinned bindings complete the real lifecycle, but that does not establish published-package support. No release version has been assigned, so the coordinated dependency update remains open.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for confirming, and for linking Kernel #443 to track the native-package publish and the package.json/package-lock.json bump. The code at this line still sends stagingAllowedLocalPaths while the dependencies stay pinned to 1.4.0, so the concern still applies. I'm keeping this thread open as the merge gate until the dependency update lands in this PR, or the backend gets a binding-support check that keeps the warning (or throws) for older bindings.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Low — This PR removes the warning that used to be logged when stagingAllowedLocalPath was ignored, and now just forwards stagingAllowedLocalPaths to the native ExecuteOptions. The kernel facade never sets isStagingOperation, so DBSQLSession never falls back to its own transfer. That leaves no safety net if the loaded native binary predates KERNEL_REV c3300aa. The PR description says the matching native packages are not published yet. With an older binary, napi drops the unknown object field, PUT/GET return instruction rows, and the call succeeds without moving any file and without logging anything.

Before this change the same situation at least produced a warn-level log. Consider keeping a warning, or adding a binding capability or version check, until the native packages and dependency pin land together. Otherwise this exact source pairing is only safe if the release-coordination step is followed.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirmed the released native package lacks this option. Matching native publication plus package.json/package-lock updates in this PR remain explicit pre-merge gates tracked in https://github.com/databricks/databricks-sql-kernel/issues/443. The source pin and local source-built validation do not establish support with published 1.4.0. A release version is not assigned yet; this PR must remain unmerged until the dependency updates and released-artifact validation are complete.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for confirming that published 1.4.0 lacks this option and for linking the gates in databricks-sql-kernel#443. Holding off on merge does cover this PR. The code here hasn't changed, though: nothing checks whether the loaded native binding supports stagingAllowedLocalPaths. If any build pairs this source with an older binary, for example through a lockfile drift or a partial upgrade, PUT/GET still succeed silently without moving any file. I'll keep this thread open as a visible pre-merge reminder. Once the dependency pin and the released-artifact validation land, it can be resolved. If you'd rather not rely only on the process gate, a capability check or a warn-level log would still be a cheap backstop.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Low — This change removes the warning for stagingAllowedLocalPath and now passes stagingAllowedLocalPaths through to the native binding. But package.json still pins the @databricks/databricks-sql-kernel-* packages at 1.4.0, and that binary was built before databricks-sql-kernel#439. napi-rs #[napi(object)] deserialization ignores fields it doesn't recognise, so if this ships against those packages the option will be dropped without any message. In that case PUT/GET/REMOVE return instruction rows instead of moving files (KernelOperationBackend always reports isStagingOperation: false, so the facade won't do the transfer either). The CHANGELOG entry already says matching native packages are required. Until the pins are bumped, consider either holding the merge or keeping a guard, e.g. a version/capability check that keeps the old warn when the loaded binding is older than the required revision. That way the old behaviour fails visibly instead of silently returning instruction rows.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Holding merge is the chosen option. Matching native publication plus package.json/package-lock bumps in this PR are explicit prerequisites under https://github.com/databricks/databricks-sql-kernel/issues/443. Latest published native is still 1.4.0; no replacement version is assigned. Source-built validation does not establish released-package support, and this PR must not merge before that gate and released-artifact validation are complete. The coordinated feature does not promise compatibility with older native packages, so no additional version/capability compatibility layer is being introduced.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for confirming. Holding the merge is one of the options I suggested, and since the coordinated feature doesn't promise compatibility with older native packages, skipping a capability shim makes sense. The pins in package.json/package-lock are still at 1.4.0 on this head, though, so the problem hasn't gone away yet. I'll leave this thread open as the visible merge gate. Please resolve it once the native packages are published and the pins bumped in this PR. Validation against those released packages should come before merge, per databricks-sql-kernel#443.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium — This PR removes the warning that stagingAllowedLocalPath is ignored on kernel, and the docs now mark the option ✅. But package.json still pins the @databricks/databricks-sql-kernel-* packages to 1.4.0, which predate databricks-sql-kernel#439. The loader never checks the binding version, and napi object conversion drops fields it doesn't know. So with the shipped binaries, stagingAllowedLocalPaths is silently dropped. The kernel returns the PUT/GET/REMOVE instruction rows, and KernelOperationBackend.getResultMetadata always reports isStagingOperation: false (line 366), so DBSQLSession doesn't do the transfer either.

The result: the caller gets no error, no log line, and no file moved. Before this PR they at least saw a warn. Until the dependency bump lands, either keep a warning or gate the forwarding on a capability or version check, so a mismatched binary can't silently skip the transfer while the docs say it's honoured.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This remains a valid pre-merge dependency finding. We are holding merge until matching native packages are published and package.json/package-lock are updated in this PR, followed by released-artifact validation, as tracked in https://github.com/databricks/databricks-sql-kernel/issues/443. No native release version is assigned yet. The feature is coordinated with the new native interface; it does not claim compatibility with old binaries, and source-build validation is not evidence that published 1.4.0 works.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for confirming. Holding merge until the matching native packages are published, package.json/package-lock are bumped in this PR, and the released artifacts are validated (databricks-sql-kernel#443) addresses the concern. The code at this line still forwards stagingAllowedLocalPaths with no version check, and the pins are still at 1.4.0, so I'm leaving the thread open until the dependency bump lands. If the native release slips and this needs to merge earlier, keep the warning or add a binding version check.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium — This change removes the warn-level 'ignoring stagingAllowedLocalPath' log and now forwards stagingAllowedLocalPaths to the native binding. But package.json still pins every @databricks/databricks-sql-kernel-* package to 1.4.0, and that release was built before the kernel revision that consumes this field. With that binding, napi drops the unknown field without an error. The facade staging handler also stays off, because the kernel backend always reports isStagingOperation: false. So with the pinned packages, a kernel-path PUT/GET/REMOVE with an allowlist moves no files, returns the presigned-URL instruction row as if it were a normal result, and logs nothing. Before this change, the user at least got a warning.

The PR description says a coordinated package bump is planned. Until that bump lands in the same release, keep a guard here: for example, keep the warning (or throw) when the loaded binding is older than the version that supports transfers. Alternatively, land this together with the package.json bump so the tree can never ship forwarding with an old binding.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed on the published-package failure and merge gate. This PR must land together with matching native-package versions and lockfile updates, as tracked in https://github.com/databricks/databricks-sql-kernel/issues/443. Version 1.4.0 does not implement the field. Local builds validate the source integration only; they do not make the current package pins releasable. No native release version is assigned, so this remains blocked before merge.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for confirming, and for linking databricks-sql-kernel#443. Since we agree this is a merge gate, I'll leave the thread open. That way it stays in the unresolved queue until the @databricks/databricks-sql-kernel-* pins and the lockfile point to a native release that reads stagingAllowedLocalPaths. If the native release could slip past this PR's merge, add a temporary guard at this spot as a safety net. For example, warn or throw when the loaded binding is older than the version that supports transfers. Once the version bump lands in this PR, this finding is resolved.

execOptions.stagingAllowedLocalPaths = Array.isArray(options.stagingAllowedLocalPath)
? options.stagingAllowedLocalPath
: [options.stagingAllowedLocalPath];
}
// Raw binding preserves qualified SQL types such as INTERVAL MONTH.
const rawParams = positionalParams ?? namedParams;
if (rawParams !== undefined) {
Expand Down
Loading
Loading