Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
13e7ed6
feat(sea): wire 7 listing metadata methods through napi binding
msrathore-db May 20, 2026
48614ec
feat(sea): wire getPrimaryKeys and getCrossReference through napi bin…
msrathore-db May 20, 2026
68b4733
feat(sea): wire getInfo through napi binding
msrathore-db May 20, 2026
b644856
Revert "feat(sea): wire getInfo through napi binding"
msrathore-db May 20, 2026
7640452
fix(sea): reconcile metadata signatures against official napi index.d.ts
msrathore-db May 20, 2026
4d2a4ff
feat(sea): wire getProcedures through napi binding
msrathore-db May 20, 2026
7d8a766
fixup(sea): drop getProcedures from IDBSQLSession / ISessionBackend p…
msrathore-db May 20, 2026
da13e8d
docs(sea): move listProcedures deferral note to SeaNativeLoader inter…
msrathore-db May 20, 2026
3d7791d
docs(sea): reformat listProcedures deferral note as NOTE comment per …
msrathore-db May 20, 2026
165a717
Revert "feat(sea): wire getProcedures through napi binding"
msrathore-db May 20, 2026
b41515a
feat(sea): validate required catalog/schema/table args upfront in get…
msrathore-db May 20, 2026
e849699
feat(sea): client-side tableTypes filter on getTables
msrathore-db May 20, 2026
3ae7ea9
test(sea): cover decodeNapiKernelError on remaining 7 metadata methods
msrathore-db May 20, 2026
3c5c8f7
docs(sea): correct M1 scope comment on SeaSessionBackend
msrathore-db May 20, 2026
870db3a
refactor(sea): drop inconsistent nativeStatement! non-null assertion
msrathore-db May 20, 2026
9c731fa
test(sea): tighten metadata error-wrap assertions to instanceOf HiveD…
msrathore-db May 20, 2026
477a369
test(sea): cover SeaTableTypeFilter fetchChunk row reduction behavior
msrathore-db May 20, 2026
f8a86d5
refactor(sea)!: move catalog/schema/sessionConf from per-statement fo…
msrathore-db May 24, 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
61 changes: 41 additions & 20 deletions lib/sea/SeaAuth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,26 +49,47 @@ const U2M_DEFAULT_REDIRECT_PORT = 8030;
* incompatible with `isolatedModules` and a runtime-coupling hazard.
* The Rust source of truth lives at `native/sea/src/database.rs`.
*/
export type SeaNativeConnectionOptions =
| {
hostName: string;
httpPath: string;
authMode: 'Pat';
token: string;
}
| {
hostName: string;
httpPath: string;
authMode: 'OAuthM2m';
oauthClientId: string;
oauthClientSecret: string;
}
| {
hostName: string;
httpPath: string;
authMode: 'OAuthU2m';
oauthRedirectPort: number;
};
/**
* Session-level defaults shared across all auth-mode variants.
*
* Mirrors `ConnectionOptions.catalog` / `.schema` / `.sessionConf` on
* the napi binding (kernel `Session::builder().defaults(DefaultOpts)`
* and `.session_conf(HashMap)` — the routes that actually populate SEA
* `CreateSession.catalog` / `.schema` / `.session_confs`).
*
* Per-statement overrides do not exist on the kernel surface; both
* pyo3 and napi expose catalog / schema / sessionConf only at session
* creation. Mirror that here so the adapter doesn't promise a
* capability the binding can't honour.
*/
export interface SeaSessionDefaults {
catalog?: string;
schema?: string;
sessionConf?: Record<string, string>;
}

export type SeaNativeConnectionOptions = SeaSessionDefaults &
(
| {
hostName: string;
httpPath: string;
authMode: 'Pat';
token: string;
}
| {
hostName: string;
httpPath: string;
authMode: 'OAuthM2m';
oauthClientId: string;
oauthClientSecret: string;
}
| {
hostName: string;
httpPath: string;
authMode: 'OAuthU2m';
oauthRedirectPort: number;
}
);

function prependSlash(str: string): string {
if (str.length > 0 && str.charAt(0) !== '/') {
Expand Down
34 changes: 21 additions & 13 deletions lib/sea/SeaBackend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,28 +89,36 @@ export default class SeaBackend implements IBackend {
throw new HiveDriverError('SeaBackend: not connected. Call connect() first.');
}

// Fold session-level defaults from the OpenSessionRequest into the
// napi `ConnectionOptions`. The kernel routes these through
// `Session::builder().defaults(DefaultOpts)` + `.session_conf(...)`
// so they land on the SEA `CreateSession` wire fields, not on each
// per-statement request. Matches pyo3's `Session.__new__` shape.
//
// Only set the optional keys when present so the napi call shape
// stays minimal — keeps wire snapshots / test assertions stable
// for callers who pass no defaults.
const sessionOptions: SeaNativeConnectionOptions = { ...this.nativeOptions };
if (request.initialCatalog !== undefined) {
sessionOptions.catalog = request.initialCatalog;
}
if (request.initialSchema !== undefined) {
sessionOptions.schema = request.initialSchema;
}
if (request.configuration !== undefined) {
sessionOptions.sessionConf = { ...request.configuration };
}

let nativeConnection: SeaNativeConnection;
try {
nativeConnection = (await this.binding.openSession(this.nativeOptions)) as SeaNativeConnection;
nativeConnection = (await this.binding.openSession(sessionOptions)) as SeaNativeConnection;
} catch (err) {
throw decodeNapiKernelError(err);
}

// Merge `request.configuration` (the existing public field for Spark
// conf) with any backend-specific session config. The SEA wire
// protocol applies these per-statement, but we capture them at
// session-open time and forward with every executeStatement to
// preserve session-config semantics.
const sessionConfig = request.configuration ? { ...request.configuration } : undefined;

return new SeaSessionBackend({
connection: nativeConnection!,
context: this.context,
defaults: {
initialCatalog: request.initialCatalog,
initialSchema: request.initialSchema,
sessionConfig,
},
});
}

Expand Down
99 changes: 85 additions & 14 deletions lib/sea/SeaNativeLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,18 +37,6 @@ import type { SeaNativeConnectionOptions } from './SeaAuth';
// eslint-disable-next-line @typescript-eslint/no-var-requires, import/no-dynamic-require, global-require
const native = require('../../native/sea/index.js');

/**
* JS-visible per-execute options carried over the napi binding boundary.
* Mirrors the `ExecuteOptions` shape generated by napi-rs into
* `native/sea/index.d.ts`. Re-declared here so the JS adapter layer
* isn't tied to the binding-generated types.
*/
export interface SeaExecuteOptions {
initialCatalog?: string;
initialSchema?: string;
sessionConfig?: Record<string, string>;
}

/**
* Arrow IPC payload returned by `Statement.fetchNextBatch()`. Carries a
* complete Arrow IPC stream (schema header + 1 record-batch message).
Expand Down Expand Up @@ -77,10 +65,93 @@ export interface SeaNativeStatement {
}

/**
* Typed surface for the opaque napi `Connection` handle.
* Typed surface for the opaque napi `Connection` handle. Signatures
* match `native/sea/index.d.ts` exactly as generated by napi-rs from
* `msrathore/krn-napi-metadata-exposure` (tip fd5e866).
*
* napi-rs emits `string | undefined | null` for every Rust `Option<String>`
* parameter — both `undefined` and `null` are accepted at the call site.
*/
export interface SeaNativeConnection {
executeStatement(sql: string, options: SeaExecuteOptions): Promise<SeaNativeStatement>;
/**
* 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.
*/
executeStatement(sql: string): Promise<SeaNativeStatement>;

// ── Metadata methods ──────────────────────────────────────────────────
/** All catalogs visible to the session. */
listCatalogs(): Promise<SeaNativeStatement>;

/** Schemas filtered by catalog (exact) and schema name LIKE pattern. */
listSchemas(
catalog?: string | undefined | null,
schemaPattern?: string | undefined | null,
): Promise<SeaNativeStatement>;

/** Tables filtered by catalog (exact), schema and table LIKE patterns, and optional type list. */
listTables(
catalog?: string | undefined | null,
schemaPattern?: string | undefined | null,
tablePattern?: string | undefined | null,
tableTypes?: Array<string> | undefined | null,
): Promise<SeaNativeStatement>;

/** Columns filtered by catalog (exact), schema/table/column LIKE patterns. */
listColumns(
catalog?: string | undefined | null,
schemaPattern?: string | undefined | null,
tablePattern?: string | undefined | null,
columnPattern?: string | undefined | null,
): Promise<SeaNativeStatement>;

/** Functions filtered by catalog (exact), schema and name LIKE patterns. */
listFunctions(
catalog?: string | undefined | null,
schemaPattern?: string | undefined | null,
functionPattern?: string | undefined | null,
): Promise<SeaNativeStatement>;

// NOTE: `listProcedures(catalog?, schemaPattern?, procedurePattern?)`
// is exposed on the napi binding for pyo3 parity, but intentionally
// not surfaced on IDBSQLSession in this M1 pass — the existing thrift
// NodeJS driver doesn't expose `getProcedures` either, and extending
// the public driver interface needs a separate spec entry + parity
// decision. Re-enable here when that decision is made.

/** All supported table types. No wire call — static result. */
listTableTypes(): Promise<SeaNativeStatement>;

/** All supported SQL data types. No wire call — static result. */
listTypeInfo(): Promise<SeaNativeStatement>;

/**
* Primary keys for the given table. All three arguments are required
* exact-match identifiers (napi rejects `undefined`/`null` with
* `InvalidArgument`). Callers must supply non-empty strings for all
* three — the JS adapter maps absent optional JS-layer fields to empty
* string or rejects early if the contract demands them.
*/
getPrimaryKeys(
catalog: string,
schema: string,
table: string,
): Promise<SeaNativeStatement>;

/**
* Foreign-key relationships. Parent side is optional (pass
* `undefined`/`null` to omit); foreign side is required.
*/
getCrossReference(
parentCatalog: string | undefined | null,
parentSchema: string | undefined | null,
parentTable: string | undefined | null,
foreignCatalog: string,
foreignSchema: string,
foreignTable: string,
): Promise<SeaNativeStatement>;

close(): Promise<void>;
}

Expand Down
Loading
Loading