Skip to content
31 changes: 29 additions & 2 deletions lib/sea/SeaNativeLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,16 +76,43 @@ export interface SeaNativeStatement {
close(): Promise<void>;
}

/**
* Per-statement options for the napi `Connection.executeStatement`.
* Mirrors the napi-rs-generated `ExecuteOptions` in
* `native/sea/index.d.ts`. Declared locally to avoid coupling the
* JS-side adapter to the auto-generated file.
*
* - `statementConf` — per-statement Spark conf overlay merged on top
* of the session-level `sessionConf` at execute time. The map wins
* on key collisions.
* - `queryTags` — the napi binding accepts a `Record<string, string>`
* and serialises it into `statementConf["query_tags"]` matching
* NodeJS Thrift's `serializeQueryTags` wire shape. The JS-side
* adapter today pre-serialises via the existing
* `serializeQueryTags` helper and writes the result into
* `statementConf` directly (so null-valued tags carry through),
* so this field is not used by the SEA backend's adapter call
* site; it is declared because the napi binding exports it and
* alternate consumers may use it.
*/
export interface SeaNativeExecuteOptions {
statementConf?: Record<string, string>;
queryTags?: Record<string, string>;
}

/**
* Typed surface for the opaque napi `Connection` handle.
*/
export interface SeaNativeConnection {
/**
* Execute a SQL statement. Catalog / schema / sessionConf are
* session-level — set on `openSession`, applied to every statement
* executed on the resulting `Connection`. No per-statement options.
* executed on the resulting `Connection`.
*
* `options` is optional; today carries `statementConf`
* (per-statement Spark conf overlay) and `queryTags`.
*/
executeStatement(sql: string): Promise<SeaNativeStatement>;
executeStatement(sql: string, options?: SeaNativeExecuteOptions): Promise<SeaNativeStatement>;
close(): Promise<void>;
}

Expand Down
16 changes: 15 additions & 1 deletion lib/sea/SeaSessionBackend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import HiveDriverError from '../errors/HiveDriverError';
import { SeaNativeConnection } from './SeaNativeLoader';
import { decodeNapiKernelError } from './SeaErrorMapping';
import SeaOperationBackend from './SeaOperationBackend';
import { serializeQueryTags } from '../utils';

export interface SeaSessionBackendOptions {
/** The opaque napi `Connection` handle returned by `openSession`. */
Expand Down Expand Up @@ -116,9 +117,22 @@ export default class SeaSessionBackend implements ISessionBackend {
);
}

// Build the per-statement conf overlay. Today only `queryTags` is
// surfaced on the public `ExecuteStatementOptions` (mirrors Thrift);
// pre-serialise on the JS side via the existing
// `serializeQueryTags` helper so the kernel-side conf overlay
// shape exactly matches the Thrift wire bytes for the same input.
// Doing the serialisation here (instead of inside the napi `queryTags`
// field) is what carries null-valued tags through correctly — napi's
// `HashMap<String, String>` can't represent nulls.
const serializedQueryTags = serializeQueryTags(options.queryTags);
const statementConf =
serializedQueryTags !== undefined ? { query_tags: serializedQueryTags } : undefined;
const nativeOptions = statementConf !== undefined ? { statementConf } : undefined;

let nativeStatement;
try {
nativeStatement = await this.connection.executeStatement(statement);
nativeStatement = await this.connection.executeStatement(statement, nativeOptions);
} catch (err) {
throw decodeNapiKernelError(err);
}
Expand Down
26 changes: 22 additions & 4 deletions native/sea/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,22 @@ export interface ConnectionOptions {
* to camelCase for free functions).
*/
export declare function openSession(options: ConnectionOptions): Promise<Connection>
/**
* Per-statement options for `Connection.executeStatement`.
* Mirrors the napi-rs-generated `ExecuteOptions` in
* `napi/src/connection.rs`. Today carries:
* - `statementConf` — per-statement Spark conf overlay merged on top
* of the session-level `sessionConf` at execute time. Map wins on
* key collisions.
* - `queryTags` — JSON-encoded into `statementConf["query_tags"]`
* matching NodeJS Thrift's `serializeQueryTags` shape. Passing both
* `queryTags` and an explicit `statementConf["query_tags"]` raises
* `InvalidArgument`.
*/
export interface ExecuteOptions {
statementConf?: Record<string, string>
queryTags?: Record<string, string>
}
/**
* A single Arrow IPC stream payload encoding one record batch (plus
* the schema header so the JS-side reader is stateless).
Expand Down Expand Up @@ -108,11 +124,13 @@ export declare class Connection {
* Execute a SQL statement and return a Statement handle that
* streams batches via `fetchNextBatch()`.
*
* No per-statement options: catalog / schema / sessionConf are
* session-level (`openSession`). Positional / named parameters
* land in M1 via `Statement::spec().param(…)` on the kernel.
* Catalog / schema / sessionConf are session-level (`openSession`).
* `options` carries per-statement knobs: `statementConf`
* (per-statement Spark conf overlay) and `queryTags` (serialised
* into `statementConf["query_tags"]` matching NodeJS Thrift's
* `serializeQueryTags` wire shape).
*/
executeStatement(sql: string): Promise<Statement>
executeStatement(sql: string, options?: ExecuteOptions | undefined | null): Promise<Statement>
/**
* Explicit close. Marks the connection wrapper as closed so
* subsequent calls on this `Connection` return `InvalidArg`, then
Expand Down
Loading
Loading