diff --git a/.gitattributes b/.gitattributes index 0a49a4b9..7f9c4ad2 100644 --- a/.gitattributes +++ b/.gitattributes @@ -36,5 +36,5 @@ Dockerfile* text # crate; regenerated by `npm run build:native`. Tell git/GitHub they're # machine-generated so they collapse in diffs and are excluded from # blame and language stats. -native/sea/index.d.ts linguist-generated=true -native/sea/index.js linguist-generated=true +native/kernel/index.d.ts linguist-generated=true +native/kernel/index.js linguist-generated=true diff --git a/.github/workflows/kernel-e2e.yml b/.github/workflows/kernel-e2e.yml index b48fdf1b..63255bc2 100644 --- a/.github/workflows/kernel-e2e.yml +++ b/.github/workflows/kernel-e2e.yml @@ -1,31 +1,31 @@ name: Kernel E2E Tests -# Runs the SEA backend e2e suite (tests/e2e/sea/**) against a real +# Runs the kernel backend e2e suite (tests/e2e/kernel/**) against a real # Databricks warehouse with a freshly-built napi-rs kernel binding. # # The kernel is a private repo with no published binary artifact. We pin # a kernel SHA in the `KERNEL_REV` file at the repo root, check the kernel # out via a GitHub App token, and run `npm run build:native` to compile -# the napi binding into native/sea/ in the same checkout the tests run +# the napi binding into native/kernel/ in the same checkout the tests run # against. Bumping `KERNEL_REV` is the ONLY way to pick up a new kernel # version — this keeps the driver <-> kernel pair bisectable, so a driver # change and the kernel revision it depends on always land together. # -# Why this exists: the committed native/sea/index.d.ts + index.js are the +# Why this exists: the committed native/kernel/index.d.ts + index.js are the # TypeScript declarations and the napi-rs platform router; the actual # `.node` binary is gitignored (large, per-platform) and is NOT in the -# repo. The standard `main.yml` e2e job has no binary, so its SEA suite +# repo. The standard `main.yml` e2e job has no binary, so its kernel suite # skips (it gates on DATABRICKS_PECOTESTING_* secrets it doesn't set). -# This workflow is what actually exercises the SEA path end-to-end against +# This workflow is what actually exercises the kernel path end-to-end against # a known kernel revision. # # Gate semantics: # - Plain PR events post a synthetic-success check so the required -# "Kernel E2E" check doesn't block PRs that don't touch the SEA path. +# "Kernel E2E" check doesn't block PRs that don't touch the kernel path. # Real tests run in the merge queue. # - `kernel-e2e` label triggers a preview run on the PR; the label is # auto-removed on `synchronize` for the same security reason. -# - merge_group fires the real gate — runs when SEA-relevant files +# - merge_group fires the real gate — runs when kernel-relevant files # changed, auto-passes otherwise. # # Required external setup (one-time, by a repo admin): @@ -81,7 +81,7 @@ jobs: # ─────────────────────────────────────────────────────────────── # Synthetic success on every non-label PR event so the required - # "Kernel E2E" check doesn't permablock PRs that don't touch SEA + # "Kernel E2E" check doesn't permablock PRs that don't touch kernel # code. Real run happens in the merge queue (or via explicit label). # ─────────────────────────────────────────────────────────────── skip-kernel-e2e-pr: @@ -112,7 +112,7 @@ jobs: }); # ─────────────────────────────────────────────────────────────── - # Detect whether SEA-relevant files changed. Used by both the + # Detect whether kernel-relevant files changed. Used by both the # labelled-PR path and the merge-queue path to decide between # "really run the suite" and "auto-pass the check". # ─────────────────────────────────────────────────────────────── @@ -148,7 +148,7 @@ jobs: ref: ${{ steps.refs.outputs.head_sha }} fetch-depth: 0 - - name: Detect SEA-relevant changes + - name: Detect kernel-relevant changes id: changed env: HEAD_SHA: ${{ steps.refs.outputs.head_sha }} @@ -157,10 +157,10 @@ jobs: CHANGED=$(git diff --name-only "$BASE_SHA" "$HEAD_SHA") echo "Changed files:" echo "$CHANGED" - # Run when the SEA driver layer, the napi binding contract, SEA + # Run when the kernel driver layer, the napi binding contract, kernel # e2e tests, this workflow, the kernel revision pin, or core deps # move. - if echo "$CHANGED" | grep -qE "^(lib/sea/|native/sea/|tests/e2e/sea/|tests/unit/sea/|\.github/workflows/kernel-e2e\.yml|KERNEL_REV|package\.json|package-lock\.json)"; then + if echo "$CHANGED" | grep -qE "^(lib/kernel/|native/kernel/|tests/e2e/kernel/|tests/unit/kernel/|\.github/workflows/kernel-e2e\.yml|KERNEL_REV|package\.json|package-lock\.json)"; then echo "run_tests=true" >> "$GITHUB_OUTPUT" else echo "run_tests=false" >> "$GITHUB_OUTPUT" @@ -168,7 +168,7 @@ jobs: # ─────────────────────────────────────────────────────────────── # Real test job. Builds the napi binding from the pinned kernel SHA - # and runs the SEA e2e suite against the dogfood warehouse. + # and runs the kernel e2e suite against the dogfood warehouse. # ─────────────────────────────────────────────────────────────── run-kernel-e2e: needs: detect-changes @@ -182,9 +182,9 @@ jobs: checks: write id-token: write env: - # The tests/e2e/sea suite reads creds from TWO sources, so we set both: + # The tests/e2e/kernel suite reads creds from TWO sources, so we set both: # - # 1. Most SEA tests (execution, results, interval-*) read the + # 1. Most kernel tests (execution, results, interval-*) read the # DATABRICKS_PECOTESTING_* vars directly and skip when absent. DATABRICKS_PECOTESTING_SERVER_HOSTNAME: ${{ secrets.DATABRICKS_HOST }} DATABRICKS_PECOTESTING_HTTP_PATH: ${{ secrets.TEST_PECO_WAREHOUSE_HTTP_PATH }} @@ -192,7 +192,7 @@ jobs: # # 2. `e2e-smoke.test.ts` imports tests/e2e/utils/config.ts, which # `process.exit(1)`s at module load if ANY E2E_* var is unset — which - # would abort the whole `tests/e2e/sea/**` mocha run before it starts. + # would abort the whole `tests/e2e/kernel/**` mocha run before it starts. # Mirror main.yml's e2e-test job mapping so the suite loads + runs. E2E_HOST: ${{ secrets.DATABRICKS_HOST }} E2E_PATH: ${{ secrets.TEST_PECO_WAREHOUSE_HTTP_PATH }} @@ -304,7 +304,7 @@ jobs: - name: Build napi binding from pinned kernel # build:native cd's into ${DATABRICKS_SQL_KERNEL_REPO}/napi, runs the - # napi-rs build, and copies index.* into native/sea/. Pointing it at + # napi-rs build, and copies index.* into native/kernel/. Pointing it at # the SHA-pinned kernel checkout is what makes the binary match # KERNEL_REV exactly. env: @@ -312,23 +312,23 @@ jobs: run: npm run build:native - name: Assert committed binding matches KERNEL_REV - # The committed native/sea/index.d.ts + index.js are the consumer-facing + # The committed native/kernel/index.d.ts + index.js are the consumer-facing # type contract + platform router; they MUST correspond to the pinned # kernel. build:native just regenerated them from the KERNEL_REV # checkout, so any diff means the committed contract drifted from the # pin — fail loudly and tell the author to commit the regenerated files. # (The .node binaries are gitignored, so git diff only sees the contract.) run: | - if ! git diff --exit-code -- native/sea/index.d.ts native/sea/index.js; then - echo "::error::native/sea/index.d.ts / index.js are out of sync with KERNEL_REV ($(tr -d '[:space:]' < KERNEL_REV)). Run 'npm run build:native' against that kernel SHA and commit native/sea/index.*." + if ! git diff --exit-code -- native/kernel/index.d.ts native/kernel/index.js; then + echo "::error::native/kernel/index.d.ts / index.js are out of sync with KERNEL_REV ($(tr -d '[:space:]' < KERNEL_REV)). Run 'npm run build:native' against that kernel SHA and commit native/kernel/index.*." exit 1 fi echo "Committed binding matches KERNEL_REV." - name: Smoke-check binding loads - run: node -e "const b=require('./native/sea'); if(typeof b.version!=='function'){throw new Error('napi binding failed to load')} console.log('kernel binding ok:', b.version())" + run: node -e "const b=require('./native/kernel'); if(typeof b.version!=='function'){throw new Error('napi binding failed to load')} console.log('kernel binding ok:', b.version())" - - name: Run SEA e2e tests + - name: Run kernel e2e tests # Run through `nyc` (not bare `mocha`) so the `require: ['ts-node/register']` # in nyc.config.js loads the .ts specs via the CommonJS ts-node hook — # exactly how main.yml's e2e job runs them. Bare `mocha` has no ts-node @@ -340,7 +340,7 @@ jobs: # index 4 (`mocha --config `): tests/e2e/.mocharc.js derives # `spec` from `process.argv.slice(4)`. We invoke mocha directly (not via # `npm run e2e -- `, whose inner shell mangles `**` to zero files). - run: NODE_OPTIONS="--max-old-space-size=4096" npx nyc --report-dir coverage_kernel_e2e mocha --config tests/e2e/.mocharc.js "tests/e2e/sea/**/*.test.ts" + run: NODE_OPTIONS="--max-old-space-size=4096" npx nyc --report-dir coverage_kernel_e2e mocha --config tests/e2e/.mocharc.js "tests/e2e/kernel/**/*.test.ts" - name: Post Kernel E2E check (success) if: success() @@ -358,7 +358,7 @@ jobs: completed_at: new Date().toISOString(), output: { title: 'Kernel E2E passed', - summary: 'tests/e2e/sea ran green against the pinned kernel SHA.' + summary: 'tests/e2e/kernel ran green against the pinned kernel SHA.' } }); @@ -383,7 +383,7 @@ jobs: }); # ─────────────────────────────────────────────────────────────── - # Auto-pass the Kernel E2E check in the merge queue when no SEA- + # Auto-pass the Kernel E2E check in the merge queue when no kernel- # relevant files changed. # ─────────────────────────────────────────────────────────────── auto-pass-merge-queue: @@ -409,7 +409,7 @@ jobs: conclusion: 'success', completed_at: new Date().toISOString(), output: { - title: 'Skipped — no SEA-relevant changes', - summary: 'No files under lib/sea/, native/sea/, tests/e2e/sea/, tests/unit/sea/, KERNEL_REV, package.json, or package-lock.json changed.' + title: 'Skipped — no kernel-relevant changes', + summary: 'No files under lib/kernel/, native/kernel/, tests/e2e/kernel/, tests/unit/kernel/, KERNEL_REV, package.json, or package-lock.json changed.' } }); diff --git a/.gitignore b/.gitignore index c3801f4b..912893fb 100644 --- a/.gitignore +++ b/.gitignore @@ -11,11 +11,11 @@ dist *.DS_Store lib/version.ts -# SEA native binding — copied/generated from kernel workspace by `npm run build:native`. -# The committed contract is `native/sea/index.d.ts` (TypeScript declarations) and -# `native/sea/index.js` (the napi-rs platform router — small, stable, and required in +# kernel native binding — copied/generated from kernel workspace by `npm run build:native`. +# The committed contract is `native/kernel/index.d.ts` (TypeScript declarations) and +# `native/kernel/index.js` (the napi-rs platform router — small, stable, and required in # the publish tarball so a missing build step can't ship a tarball that can't load). # The `.node` binaries are large per-platform artifacts and must NOT be committed; # in production they arrive via the `@databricks/sql-kernel-` optional deps. -native/sea/index.node -native/sea/index.*.node +native/kernel/index.node +native/kernel/index.*.node diff --git a/.npmignore b/.npmignore index 448289a7..d9b7fdec 100644 --- a/.npmignore +++ b/.npmignore @@ -3,12 +3,12 @@ !dist/**/* !thrift/**/* -# SEA napi-rs router shim + TypeScript declarations. The router (index.js) +# kernel napi-rs router shim + TypeScript declarations. The router (index.js) # selects the per-platform `.node` artifact from `@databricks/sql-kernel-*` # optionalDependencies (populated when the kernel CI publishes them); # the .d.ts is the consumer-facing type contract. -!native/sea/index.js -!native/sea/index.d.ts +!native/kernel/index.js +!native/kernel/index.d.ts !LICENSE !NOTICE diff --git a/.prettierignore b/.prettierignore index 4a764095..afcb3d3f 100644 --- a/.prettierignore +++ b/.prettierignore @@ -15,5 +15,5 @@ package-lock.json # Generated by napi-rs from the kernel's `napi-binding/napi/` crate; # regenerated by `npm run build:native`. Format follows napi-rs's # defaults (no semicolons), not this repo's prettier config. -native/sea/index.d.ts -native/sea/index.js +native/kernel/index.d.ts +native/kernel/index.js diff --git a/lib/DBSQLClient.ts b/lib/DBSQLClient.ts index c3506680..d96d6692 100644 --- a/lib/DBSQLClient.ts +++ b/lib/DBSQLClient.ts @@ -18,7 +18,7 @@ import { buildUserAgentString } from './utils'; import IBackend from './contracts/IBackend'; import { InternalConnectionOptions } from './contracts/InternalConnectionOptions'; import ThriftBackend from './thrift-backend/ThriftBackend'; -import SeaBackend from './sea/SeaBackend'; +import KernelBackend from './kernel/KernelBackend'; import PlainHttpAuthentication from './connection/auth/PlainHttpAuthentication'; import DatabricksOAuth, { OAuthFlow } from './connection/auth/DatabricksOAuth'; import { @@ -627,12 +627,12 @@ export default class DBSQLClient extends EventEmitter implements IDBSQLClient, I this.connectionProvider = this.createConnectionProvider(options); - // M0: `useSEA` is consumed via a non-exported internal-options cast so it - // doesn't ship in the public `.d.ts`. Mirrors Python's `kwargs.get("use_sea")` + // M0: `useKernel` is consumed via a non-exported internal-options cast so it + // doesn't ship in the public `.d.ts`. Mirrors Python's `kwargs.get("use_kernel")` // pattern (see databricks-sql-python/src/databricks/sql/session.py). const internalOptions = options as ConnectionOptions & InternalConnectionOptions; - const backend = internalOptions.useSEA - ? new SeaBackend({ context: this }) + const backend = internalOptions.useKernel + ? new KernelBackend({ context: this }) : new ThriftBackend({ context: this, onConnectionEvent: (event, payload) => this.forwardConnectionEvent(event, payload), diff --git a/lib/DBSQLLogger.ts b/lib/DBSQLLogger.ts index 38863a6d..3d298b9b 100644 --- a/lib/DBSQLLogger.ts +++ b/lib/DBSQLLogger.ts @@ -9,7 +9,7 @@ export default class DBSQLLogger implements IDBSQLLogger { file?: winston.transports.FileTransportInstance; }; - // Subscribers notified on `setLevel(...)` — used by the SEA/kernel backend to + // Subscribers notified on `setLevel(...)` — used by the kernel backend to // keep the kernel-side log bridge's verbosity in lock-step with this logger. private levelListeners: Array<(level: LogLevel) => void> = []; diff --git a/lib/DBSQLOperation.ts b/lib/DBSQLOperation.ts index 484e5bcd..1ec1922c 100644 --- a/lib/DBSQLOperation.ts +++ b/lib/DBSQLOperation.ts @@ -171,7 +171,7 @@ export default class DBSQLOperation implements IOperation { /** * Requests operation status. Returns the Thrift wire response for * back-compat with existing user code. On the Thrift backend the response - * is returned verbatim; on any other backend (e.g. SEA) the response is + * is returned verbatim; on any other backend (e.g. kernel) the response is * synthesized from the neutral {@link IOperationBackend.status} result, * with Thrift-only fields (`taskStatus`, `numModifiedRows`, etc.) left * undefined. diff --git a/lib/contracts/IBackend.ts b/lib/contracts/IBackend.ts index 2e5edd16..91423063 100644 --- a/lib/contracts/IBackend.ts +++ b/lib/contracts/IBackend.ts @@ -3,14 +3,14 @@ import ISessionBackend from './ISessionBackend'; /** * Top-level backend dispatch handle. One instance per `DBSQLClient`, - * chosen at `connect()` time based on the `useSEA` flag and never + * chosen at `connect()` time based on the `useKernel` flag and never * re-selected per-call. */ export default interface IBackend { /** * Establish backend-level state before any session is opened. Implementations * consume `options` to build backend-specific connection parameters (e.g. the - * SEA backend derives napi-binding `SeaNativeConnectionOptions` from the auth + * kernel backend derives napi-binding `KernelNativeConnectionOptions` from the auth * + host fields here). Transport-layer connection providers are owned by * `DBSQLClient` (via `IClientContext`) and exposed to backends through * constructor injection. diff --git a/lib/contracts/IDBSQLLogger.ts b/lib/contracts/IDBSQLLogger.ts index 23ab3df3..cbf73b6b 100644 --- a/lib/contracts/IDBSQLLogger.ts +++ b/lib/contracts/IDBSQLLogger.ts @@ -7,7 +7,7 @@ export default interface IDBSQLLogger { log(level: LogLevel, message: string): void; /** - * Optional: the logger's current level. When implemented, the SEA/kernel + * Optional: the logger's current level. When implemented, the kernel * backend uses it to set the verbosity of the kernel-side (Rust) log bridge, * so kernel logs are filtered at the same level as the driver's own logs and * land in the same sink. Loggers that don't implement it leave the kernel @@ -17,7 +17,7 @@ export default interface IDBSQLLogger { /** * Optional: subscribe to runtime level changes. When implemented, the - * SEA/kernel backend subscribes so a runtime `setLevel(...)` retargets the + * kernel backend subscribes so a runtime `setLevel(...)` retargets the * kernel-side log bridge too (not just the driver's own transports) — keeping * kernel verbosity in lock-step with the driver's. Returns an unsubscribe * function. Loggers that don't implement it still get the connect-time level; diff --git a/lib/contracts/IDBSQLSession.ts b/lib/contracts/IDBSQLSession.ts index d17cfa5a..e7eb31d8 100644 --- a/lib/contracts/IDBSQLSession.ts +++ b/lib/contracts/IDBSQLSession.ts @@ -19,7 +19,7 @@ export type ExecuteStatementOptions = { * - **Thrift backend:** no-op. The Thrift path always submits asynchronously * (`runAsync: true` on the wire) and polls during fetch; this option is not * read. - * - **Kernel backend (`useSEA`):** selects the kernel execution path — + * - **Kernel backend (`useKernel`):** selects the kernel execution path — * `false`/unset (default) runs the blocking direct-results path (faster, * cancellable mid-compute); `true` submits and polls (returns a pending * handle before completion). Default is sync, matching the python @@ -39,13 +39,13 @@ export type ExecuteStatementOptions = { */ queryTags?: Record; /** - * SEA-only: server-side row cap for this statement (kernel `row_limit`). The + * kernel-only: server-side row cap for this statement (kernel `row_limit`). The * Thrift backend has no execute-time server cap, so this is a no-op there; * use `maxRows` for the cross-backend client-side fetch limit. */ rowLimit?: number; /** - * SEA-only: per-statement Spark conf overlay (kernel `statement_conf`). + * kernel-only: per-statement Spark conf overlay (kernel `statement_conf`). * Merged with the serialized `queryTags` (which land under the reserved * `query_tags` key). Ignored by the Thrift backend. */ diff --git a/lib/contracts/InternalConnectionOptions.ts b/lib/contracts/InternalConnectionOptions.ts index 24575984..ffc3d7ec 100644 --- a/lib/contracts/InternalConnectionOptions.ts +++ b/lib/contracts/InternalConnectionOptions.ts @@ -2,12 +2,12 @@ * Internal, non-exported extension of `ConnectionOptions`. Carries M0-only * flags that should not appear in the published `.d.ts`. * - * Matches the Python connector pattern: there, `use_sea` is consumed via - * `kwargs.get("use_sea", False)` and is intentionally absent from the typed + * Matches the Python connector pattern: there, `use_kernel` is consumed via + * `kwargs.get("use_kernel", False)` and is intentionally absent from the typed * signature (see `databricks-sql-python/src/databricks/sql/session.py`). * * Callers cast `ConnectionOptions` to this type *only* at the read site - * inside the driver; user code that wants to set `useSEA` may still do so + * inside the driver; user code that wants to set `useKernel` may still do so * via an untyped object literal — the option is not part of the public * contract and may be removed without notice. */ @@ -17,28 +17,28 @@ export interface InternalConnectionOptions { * backend instead of the default Thrift backend. Defaults to `false`. * @internal Not stable; M0 stub only. */ - useSEA?: boolean; + useKernel?: boolean; /** - * SEA-only: kernel connection-pool size (`ConnectionOptions.max_connections`). + * kernel-only: kernel connection-pool size (`ConnectionOptions.max_connections`). * Validated as a positive integer within the napi `u32` range. - * @internal SEA path only. + * @internal kernel path only. */ maxConnections?: number; /** - * SEA-only: verify the server's TLS certificate. Secure-by-default — omit + * kernel-only: verify the server's TLS certificate. Secure-by-default — omit * to keep full chain + hostname verification; set `false` only to opt into * the insecure accept-anything mode. - * @internal SEA path only. + * @internal kernel path only. */ checkServerCertificate?: boolean; /** - * SEA-only: PEM-encoded CA certificate (string or `Buffer`) added to the + * kernel-only: PEM-encoded CA certificate (string or `Buffer`) added to the * trust store on top of the system roots — for TLS-inspecting proxies or * on-prem internal CAs. Honoured regardless of `checkServerCertificate`. - * @internal SEA path only. + * @internal kernel path only. */ customCaCert?: Buffer | string; } diff --git a/lib/contracts/OperationStatus.ts b/lib/contracts/OperationStatus.ts index 7f167aba..47279307 100644 --- a/lib/contracts/OperationStatus.ts +++ b/lib/contracts/OperationStatus.ts @@ -1,6 +1,6 @@ /** * Backend-neutral operation state. Mirrors the kernel/pyo3 `StatementStatus` - * and the Python connector's `CommandState`, so a SEA `IOperationBackend` + * and the Python connector's `CommandState`, so a kernel `IOperationBackend` * implementer can return these without depending on the Thrift wire enum. * * Thrift mapping (in `ThriftOperationBackend.adaptOperationStatus`): diff --git a/lib/contracts/ResultMetadata.ts b/lib/contracts/ResultMetadata.ts index 5fc09a79..e332cb33 100644 --- a/lib/contracts/ResultMetadata.ts +++ b/lib/contracts/ResultMetadata.ts @@ -3,7 +3,7 @@ import { TTableSchema } from '../../thrift/TCLIService_types'; /** * Backend-neutral result-format taxonomy. Mirrors the three on-wire shapes * `ThriftOperationBackend` actually dispatches on (`COLUMN_BASED_SET`, - * `ARROW_BASED_SET`, `URL_BASED_SET`); a SEA implementer surfaces the same + * `ARROW_BASED_SET`, `URL_BASED_SET`); a kernel implementer surfaces the same * three so result-handling stays format-agnostic. */ export enum ResultFormat { @@ -18,7 +18,7 @@ export enum ResultFormat { * `schema` keeps the Thrift `TTableSchema` shape for now because the public * `DBSQLOperation.getSchema()` and `getMetadata()` already expose it on * `IOperation`; carrying it across the boundary preserves back-compat. The - * SEA backend will adapt its column descriptors into the same shape until + * kernel backend will adapt its column descriptors into the same shape until * the public IOperation surface is migrated in a later PR. */ export interface ResultMetadata { diff --git a/lib/sea/SeaArrowIpc.ts b/lib/kernel/KernelArrowIpc.ts similarity index 94% rename from lib/sea/SeaArrowIpc.ts rename to lib/kernel/KernelArrowIpc.ts index 6e09dcb4..6b7cd369 100644 --- a/lib/sea/SeaArrowIpc.ts +++ b/lib/kernel/KernelArrowIpc.ts @@ -14,7 +14,7 @@ import { RecordBatchReader, MessageReader, MessageHeader, Schema, Field, DataType, TypeMap } from 'apache-arrow'; import { TTableSchema, TTypeId, TPrimitiveTypeEntry } from '../../thrift/TCLIService_types'; -import { rewriteDurationToInt64, DURATION_UNIT_METADATA_KEY } from './SeaArrowIpcDurationFix'; +import { rewriteDurationToInt64, DURATION_UNIT_METADATA_KEY } from './KernelArrowIpcDurationFix'; import HiveDriverError from '../errors/HiveDriverError'; /** @@ -29,7 +29,7 @@ const DATABRICKS_TYPE_NAME = 'databricks.type_name'; * * Why this exists: `ArrowResultConverter` consumes `ArrowBatch` objects * that carry an explicit `rowCount`, but the kernel's IPC payload only - * carries per-RecordBatch `length` (no separate total). `SeaResultsProvider` + * carries per-RecordBatch `length` (no separate total). `KernelResultsProvider` * needs that count to build the `ArrowBatch` it hands to the converter — * which then re-decodes the same bytes for the actual values. * @@ -80,7 +80,7 @@ export function decodeIpcSchema(ipcBytes: Buffer): Schema { // and surface a raw TypeError instead of a typed driver error. The // real kernel always materialises a schema, so this is defensive. if (!reader.schema) { - throw new HiveDriverError('SEA result: Arrow IPC stream carried no schema (empty or truncated payload)'); + throw new HiveDriverError('kernel result: Arrow IPC stream carried no schema (empty or truncated payload)'); } return reader.schema; } @@ -89,12 +89,12 @@ export function decodeIpcSchema(ipcBytes: Buffer): Schema { * Pre-process raw IPC bytes from the kernel so they're consumable by * `apache-arrow@13`. The current transformation is `Duration → Int64` * with the original duration unit preserved in field metadata (see - * `SeaArrowIpcDurationFix.ts`). Returned bytes are byte-identical to + * `KernelArrowIpcDurationFix.ts`). Returned bytes are byte-identical to * the input when no transformation is needed. * * Exposed so callers can pre-patch the buffer **once** and pass the * result through both `decodeIpcBatch` (for row-count extraction in - * `SeaResultsProvider`) and `ArrowResultConverter.fetchNext` (which + * `KernelResultsProvider`) and `ArrowResultConverter.fetchNext` (which * re-decodes the same bytes via `RecordBatchReader.from`). Without * this, the converter would re-throw on `Duration` because it never * sees the patched bytes. @@ -109,7 +109,7 @@ export function patchIpcBytes(ipcBytes: Buffer): Buffer { * * This is the synthesis step that lets the existing * `ArrowResultConverter` Phase-2 dispatch (`convertThriftValue` in - * `lib/result/utils.ts:61-98`) keep working unchanged for the SEA + * `lib/result/utils.ts:61-98`) keep working unchanged for the kernel * path. Phase-2 keys exclusively off `TPrimitiveTypeEntry.type` per * column, so we synthesize a `TColumnDesc` whose `TTypeId` matches the * server-emitted Arrow type as closely as possible. @@ -203,7 +203,7 @@ function arrowTypeToTTypeId(field: Field): TTypeId { if (DataType.isInt(arrowType)) { // Duration columns are rewritten to Int64 with a // `databricks.arrow.duration_unit` metadata marker (see - // `SeaArrowIpcDurationFix.ts`). Surface them as STRING_TYPE (matching the + // `KernelArrowIpcDurationFix.ts`). Surface them as STRING_TYPE (matching the // Thrift backend and Python kernel) — the converter still formats the // value into the thrift INTERVAL DAY-TIME string via the duration_unit // metadata, independent of this type code. @@ -255,7 +255,7 @@ function arrowTypeToTTypeId(field: Field): TTypeId { /** * Synthesize a Thrift `TTableSchema` from an Arrow schema decoded out - * of the kernel's IPC stream. Used by `SeaOperationBackend.getResultMetadata` + * of the kernel's IPC stream. Used by `KernelOperationBackend.getResultMetadata` * to drive `ArrowResultConverter.convertThriftTypes` (Phase 2) without * changing that code. */ diff --git a/lib/sea/SeaArrowIpcDurationFix.ts b/lib/kernel/KernelArrowIpcDurationFix.ts similarity index 97% rename from lib/sea/SeaArrowIpcDurationFix.ts rename to lib/kernel/KernelArrowIpcDurationFix.ts index 84349faa..3c91c206 100644 --- a/lib/sea/SeaArrowIpcDurationFix.ts +++ b/lib/kernel/KernelArrowIpcDurationFix.ts @@ -18,10 +18,10 @@ * (FlatBuffer `Type` enum id 18) in version 14. * * The Databricks SQL server emits INTERVAL DAY-TIME columns as Arrow - * `Duration(MICROSECOND)` in the SEA IPC stream. apache-arrow@13's + * `Duration(MICROSECOND)` in the kernel IPC stream. apache-arrow@13's * `decodeFieldType` (`node_modules/apache-arrow/ipc/metadata/message.js:339-397`) * throws `Unrecognized type: "Duration" (18)` on the schema FlatBuffer - * before any record batch is read, breaking the entire SEA path for any + * before any record batch is read, breaking the entire kernel path for any * result that contains an INTERVAL DAY-TIME column. * * Because the physical layout of an Arrow `Duration` column is @@ -34,18 +34,18 @@ * rewritten field's `custom_metadata` under the key * `databricks.arrow.duration_unit`. * - * **Scope on this layer (SEA execution + results, PR 2/3):** the rewrite's + * **Scope on this layer (kernel execution + results, PR 2/3):** the rewrite's * job here is purely to make the stream *decodable* by apache-arrow@13. The * resulting Int64 surfaces to callers as a raw microsecond/nanosecond number * on this layer. The consumer that reads the `duration_unit` marker and * formats it back into the thrift-equivalent string (e.g. * `"1 02:03:04.000000000"`) lands in PR 3/3 (#411) — verified against a live * warehouse to produce byte-identical output to the Thrift path. Until that - * consumer merges, INTERVAL DAY-TIME columns are raw Int64 under SEA. + * consumer merges, INTERVAL DAY-TIME columns are raw Int64 under kernel. * * Why this lives in its own file: the rewriter is the only place in the * codebase that needs to construct FlatBuffers by hand using the - * `flatbuffers` library; isolating it keeps `SeaArrowIpc.ts` focused on + * `flatbuffers` library; isolating it keeps `KernelArrowIpc.ts` focused on * the high-level Arrow-decoded views. * * @see lib/result/ArrowResultConverter.ts — the Phase-1 INTERVAL formatter @@ -257,9 +257,9 @@ function maybeRewriteSchemaMessage(schemaMessageBytes: Buffer): Buffer | null { // STRUCT/ARRAY/MAP is left untouched and apache-arrow@13 then throws // `Unrecognized type: "Duration" (18)` when decoding the batch. // - // This is a SHARED apache-arrow@13 limitation, NOT a SEA-specific gap: + // This is a SHARED apache-arrow@13 limitation, NOT a kernel-specific gap: // verified against a live warehouse, the Thrift backend throws the - // identical error for `array(INTERVAL '1' SECOND)` — so SEA matches Thrift + // identical error for `array(INTERVAL '1' SECOND)` — so kernel matches Thrift // here rather than diverging. Lifting it (recurse the rewrite into // children) is deferred until there's a real-world nested-Duration payload // to validate against, and would ideally land on both backends together. @@ -332,7 +332,7 @@ function rebuildSchemaWithDurationRewritten(message: Message, fbSchema: FbSchema // bigint vector; for the kernel's payloads this is typically empty // so we skip it. If a non-empty features vector appears, we drop it // (Arrow features encode optional compression flags; the kernel - // emits uncompressed streams for the SEA path per + // emits uncompressed streams for the kernel path per // `findings/rust-kernel/M0-kernel-async-readiness-2026-05-15.md`). FbSchema.startSchema(builder); FbSchema.addEndianness(builder, fbSchema.endianness()); diff --git a/lib/sea/SeaAuth.ts b/lib/kernel/KernelAuth.ts similarity index 81% rename from lib/sea/SeaAuth.ts rename to lib/kernel/KernelAuth.ts index a9d9d116..96427b70 100644 --- a/lib/sea/SeaAuth.ts +++ b/lib/kernel/KernelAuth.ts @@ -20,7 +20,7 @@ import HiveDriverError from '../errors/HiveDriverError'; /** * Default local listener port for the U2M authorization-code callback. * Hardcoded here so the override of the kernel default (8020) to the - * thrift default (8030) is invariant for SEA callers — preserving parity + * thrift default (8030) is invariant for kernel callers — preserving parity * with the existing Node driver. Not exposed on the public * `ConnectionOptions` (thrift hides `callbackPorts` from its public * surface too — see nodejs-thrift-expert survey §B.2). @@ -29,7 +29,7 @@ const U2M_DEFAULT_REDIRECT_PORT = 8030; /** * Shape consumed by the napi-binding's `openSession()` (see - * `native/sea/index.d.ts`). Mirrors `ConnectionOptions` in the binding's + * `native/kernel/index.d.ts`). Mirrors `ConnectionOptions` in the binding's * `.d.ts`; declared locally to avoid coupling the JS-side adapter to the * auto-generated TS file. * @@ -45,17 +45,17 @@ const U2M_DEFAULT_REDIRECT_PORT = 8030; * variant names verbatim (`'Pat'`, `'OAuthM2m'`, `'OAuthU2m'` — napi-rs's * `#[napi(string_enum)]` without an explicit case option emits the * Rust variant identifier as-is). We duplicate the values here instead - * of importing `AuthMode` from `native/sea/index.d.ts` because that + * of importing `AuthMode` from `native/kernel/index.d.ts` because that * file declares `AuthMode` as `export const enum`, which is * incompatible with `isolatedModules` and a runtime-coupling hazard. - * The Rust source of truth lives at `native/sea/src/database.rs`. + * The Rust source of truth lives at `native/kernel/src/database.rs`. */ /** * 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 + * and `.session_conf(HashMap)` — the routes that actually populate kernel * `CreateSession.catalog` / `.schema` / `.session_confs`). * * Per-statement overrides do not exist on the kernel surface; both @@ -63,7 +63,7 @@ const U2M_DEFAULT_REDIRECT_PORT = 8030; * creation. Mirror that here so the adapter doesn't promise a * capability the binding can't honour. */ -export interface SeaSessionDefaults { +export interface KernelSessionDefaults { catalog?: string; schema?: string; sessionConf?: Record; @@ -71,7 +71,7 @@ export interface SeaSessionDefaults { * Render `INTERVAL` / `DURATION` result columns as strings * (kernel `ResultConfig.intervals_as_string`). The kernel default is * native Arrow `month_interval` / `duration[us]`, but the NodeJS - * Thrift driver surfaces intervals as strings — so the SEA path sets + * Thrift driver surfaces intervals as strings — so the kernel path sets * this `true` so its result shape is a byte-compatible drop-in for the * Thrift backend. Omitting it falls back to the kernel's native types. */ @@ -79,7 +79,7 @@ export interface SeaSessionDefaults { /** * Render complex (`ARRAY` / `MAP` / `STRUCT` / `VARIANT`) result * columns as JSON strings (kernel `ResultConfig.complex_types_as_json`). - * Left unset on the SEA path: native Arrow nested types already decode + * Left unset on the kernel path: native Arrow nested types already decode * identically to the Thrift backend through the shared Arrow converter, * so forcing JSON here would *introduce* a divergence rather than * remove one. @@ -88,7 +88,7 @@ export interface SeaSessionDefaults { /** * Per-session kernel connection-pool size * (kernel `ConnectionOptions.max_connections`). Validated as a positive - * integer within the napi `u32` range by `buildSeaConnectionOptions`. + * integer within the napi `u32` range by `buildKernelConnectionOptions`. */ maxConnections?: number; } @@ -100,12 +100,12 @@ export interface SeaSessionDefaults { * * The napi shape takes `customCaCert` as a `Buffer` only; the public * `ConnectionOptions` additionally accepts a PEM string, which - * `buildSeaConnectionOptions` normalises to a `Buffer` before crossing + * `buildKernelConnectionOptions` normalises to a `Buffer` before crossing * the FFI boundary. */ -export interface SeaTlsOptions { +export interface KernelTlsOptions { /** - * Verify the server's TLS certificate. The SEA backend is + * Verify the server's TLS certificate. The kernel backend is * **secure-by-default**: omitting this leaves the kernel default of * `true` (full chain + hostname verification). Set `false` only to opt * into the insecure, accept-anything mode (analogous to Thrift's @@ -117,8 +117,8 @@ export interface SeaTlsOptions { customCaCert?: Buffer; } -export type SeaNativeConnectionOptions = SeaSessionDefaults & - SeaTlsOptions & +export type KernelNativeConnectionOptions = KernelSessionDefaults & + KernelTlsOptions & ( | { hostName: string; @@ -181,13 +181,13 @@ const MAX_U32 = 0xffffffff; * Throws `HiveDriverError` when `customCaCert` is supplied but empty or * (for strings) lacks a PEM certificate header. */ -export function buildSeaTlsOptions(options: ConnectionOptions): SeaTlsOptions { - // Read the SEA-only fields through the purpose-built internal options type +export function buildKernelTlsOptions(options: ConnectionOptions): KernelTlsOptions { + // Read the kernel-only fields through the purpose-built internal options type // rather than an ad-hoc inline cast, so the shape can't silently drift from // its declaration and a typo'd key fails to compile. const { checkServerCertificate, customCaCert } = options as ConnectionOptions & InternalConnectionOptions; - const tls: SeaTlsOptions = {}; + const tls: KernelTlsOptions = {}; if (checkServerCertificate !== undefined) { tls.checkServerCertificate = checkServerCertificate; @@ -202,7 +202,7 @@ export function buildSeaTlsOptions(options: ConnectionOptions): SeaTlsOptions { // two independent substring checks. Full parsing is deferred to the kernel. if (!/-----BEGIN CERTIFICATE-----[\s\S]+?-----END CERTIFICATE-----/.test(customCaCert)) { throw new HiveDriverError( - 'SEA backend: `customCaCert` string does not look like a PEM certificate ' + + 'kernel backend: `customCaCert` string does not look like a PEM certificate ' + "(expected a '-----BEGIN CERTIFICATE-----' … '-----END CERTIFICATE-----' block). " + 'Pass PEM text or a Buffer of PEM bytes.', ); @@ -210,11 +210,11 @@ export function buildSeaTlsOptions(options: ConnectionOptions): SeaTlsOptions { tls.customCaCert = Buffer.from(customCaCert, 'utf8'); } else if (Buffer.isBuffer(customCaCert)) { if (customCaCert.length === 0) { - throw new HiveDriverError('SEA backend: `customCaCert` Buffer is empty.'); + throw new HiveDriverError('kernel backend: `customCaCert` Buffer is empty.'); } tls.customCaCert = customCaCert; } else { - throw new HiveDriverError('SEA backend: `customCaCert` must be a PEM string or a Buffer.'); + throw new HiveDriverError('kernel backend: `customCaCert` must be a PEM string or a Buffer.'); } } @@ -240,18 +240,18 @@ export function buildSeaTlsOptions(options: ConnectionOptions): SeaTlsOptions { * **Flow selection — DELIBERATE DIVERGENCE FROM THRIFT.** Thrift's * `DBSQLClient.createAuthProvider` (`DBSQLClient.ts:216`) keys off the * *secret* (`oauthClientSecret === undefined ? U2M : M2M`), so a custom - * `oauthClientId` with no secret runs U2M with that id. SEA instead keys + * `oauthClientId` with no secret runs U2M with that id. kernel instead keys * off `oauthClientId` *presence* (id present → M2M, absent → U2M). The * trade-off: keying off the id means a caller who set an id but * typoed/forgot the secret gets the actionable M2M "secret is required" * error instead of being silently routed to U2M (which would hide their * intent). The cost is two real behavioural gaps vs Thrift: - * 1. `oauthClientId` + no secret → Thrift runs U2M; SEA throws + * 1. `oauthClientId` + no secret → Thrift runs U2M; kernel throws * `AuthenticationError` (M2M secret required). - * 2. SEA U2M has NO custom-client-id support — the kernel hardcodes - * `client_id = "databricks-cli"`, and SEA rejects any `oauthClientId` + * 2. kernel U2M has NO custom-client-id support — the kernel hardcodes + * `client_id = "databricks-cli"`, and kernel rejects any `oauthClientId` * on the U2M arm. Thrift U2M honours a custom `clientId`. - * Both are documented limitations of the M0 SEA OAuth surface, not bugs. + * Both are documented limitations of the M0 kernel OAuth surface, not bugs. * * Out of scope on the OAuth paths (rejected with a clear error): * - `azureTenantId` / `useDatabricksOAuthInAzure` → Microsoft Entra @@ -274,7 +274,7 @@ export function buildSeaTlsOptions(options: ConnectionOptions): SeaTlsOptions { * - `HiveDriverError` for unsupported auth modes / Azure-direct / * custom persistence / ambiguous combinations. */ -export function buildSeaConnectionOptions(options: ConnectionOptions): SeaNativeConnectionOptions { +export function buildKernelConnectionOptions(options: ConnectionOptions): KernelNativeConnectionOptions { const { authType } = options as { authType?: string }; const base: { @@ -282,32 +282,34 @@ export function buildSeaConnectionOptions(options: ConnectionOptions): SeaNative httpPath: string; intervalsAsString: boolean; maxConnections?: number; - } & SeaTlsOptions = { + } & KernelTlsOptions = { hostName: options.host, httpPath: prependSlash(options.path), // Match the NodeJS Thrift driver, which surfaces INTERVAL columns as // strings. The kernel defaults to native Arrow interval/duration types; - // forcing the string rendering here keeps the SEA path a byte-compatible + // forcing the string rendering here keeps the kernel path a byte-compatible // drop-in. Complex types are intentionally left at the kernel default // (native Arrow) — they already decode identically to Thrift via the // shared Arrow converter, so `complexTypesAsJson` is not forced on. intervalsAsString: true, // TLS knobs (server-cert verification toggle + custom CA). Validated and // normalised (string PEM → Buffer) here so the napi shape only sees a Buffer. - ...buildSeaTlsOptions(options), + ...buildKernelTlsOptions(options), }; - // SEA-only pool sizing; read via cast to match how this function reads the - // other SEA-specific options (TLS) — they live on the internal options + // kernel-only pool sizing; read via cast to match how this function reads the + // other kernel-specific options (TLS) — they live on the internal options // surface, not the published public `ConnectionOptions` `.d.ts`. const { maxConnections } = options as ConnectionOptions & InternalConnectionOptions; if (maxConnections !== undefined) { if (!Number.isInteger(maxConnections) || maxConnections < 1) { - throw new HiveDriverError(`SEA backend: \`maxConnections\` must be a positive integer; got ${maxConnections}.`); + throw new HiveDriverError( + `kernel backend: \`maxConnections\` must be a positive integer; got ${maxConnections}.`, + ); } if (maxConnections > MAX_U32) { throw new HiveDriverError( - `SEA backend: \`maxConnections\` exceeds the napi u32 limit (${MAX_U32}); got ${maxConnections}. ` + + `kernel backend: \`maxConnections\` exceeds the napi u32 limit (${MAX_U32}); got ${maxConnections}. ` + 'Typical pool sizes are 10-500.', ); } @@ -326,12 +328,12 @@ export function buildSeaConnectionOptions(options: ConnectionOptions): SeaNative const { token } = options as { token?: string }; if (typeof token !== 'string' || isBlankOrReserved(token)) { throw new AuthenticationError( - "SEA backend: a non-empty PAT must be supplied via `token` when using `authType: 'access-token'`.", + "kernel backend: a non-empty PAT must be supplied via `token` when using `authType: 'access-token'`.", ); } if (oauth.oauthClientId !== undefined || oauth.oauthClientSecret !== undefined) { throw new HiveDriverError( - 'SEA backend: cannot supply both `token` and `oauthClientId`/`oauthClientSecret` ' + + 'kernel backend: cannot supply both `token` and `oauthClientId`/`oauthClientSecret` ' + "on the same connection. Pick one: 'access-token' (PAT) uses `token`; " + "'databricks-oauth' uses the OAuth fields.", ); @@ -342,14 +344,14 @@ export function buildSeaConnectionOptions(options: ConnectionOptions): SeaNative if (authType === 'databricks-oauth') { if ((options as { token?: string }).token !== undefined) { throw new HiveDriverError( - "SEA backend: cannot supply `token` alongside `authType: 'databricks-oauth'`. " + + "kernel backend: cannot supply `token` alongside `authType: 'databricks-oauth'`. " + "Use `authType: 'access-token'` for PAT, or omit `token` to use OAuth.", ); } if (oauth.azureTenantId !== undefined || oauth.useDatabricksOAuthInAzure === true) { throw new HiveDriverError( - 'SEA backend: Azure-direct OAuth (azureTenantId / useDatabricksOAuthInAzure) ' + + 'kernel backend: Azure-direct OAuth (azureTenantId / useDatabricksOAuthInAzure) ' + 'is not supported. The workspace-OIDC discovery path handles Azure workspaces ' + 'today without these options.', ); @@ -357,13 +359,13 @@ export function buildSeaConnectionOptions(options: ConnectionOptions): SeaNative // Flow selector — DELIBERATELY DIFFERENT from thrift's // `DBSQLClient.createAuthProvider` (`DBSQLClient.ts:216`), which keys off - // the secret (`oauthClientSecret === undefined ? U2M : M2M`). SEA keys off + // the secret (`oauthClientSecret === undefined ? U2M : M2M`). kernel keys off // `oauthClientId` *presence* (the "do I have an id?" signal) instead, so a // user who set an id but typoed/forgot the secret gets the actionable M2M // "secret is required" error rather than being silently routed to U2M // (which would hide their intent). Cost: `id + no secret` throws here - // where thrift would run U2M, and SEA U2M has no custom-client-id support - // (see buildSeaConnectionOptions header). The U2M arm still defends against an id + // where thrift would run U2M, and kernel U2M has no custom-client-id support + // (see buildKernelConnectionOptions header). The U2M arm still defends against an id // sneaking through: fires only when `oauthClientId` is provided as // a blank-reserved literal (e.g., whitespace, `"null"`, `"undefined"`) // alongside an absent/blank secret — both `idIsBlank` and @@ -384,14 +386,14 @@ export function buildSeaConnectionOptions(options: ConnectionOptions): SeaNative // The kernel hardcodes `client_id = "databricks-cli"` for U2M; // there's no JS-side override knob. throw new HiveDriverError( - 'SEA backend: `oauthClientId` is not supported on the OAuth U2M flow; ' + + 'kernel backend: `oauthClientId` is not supported on the OAuth U2M flow; ' + "the kernel uses the built-in 'databricks-cli' client. " + 'Omit `oauthClientId` for U2M, or supply `oauthClientSecret` for the M2M flow.', ); } if (oauth.persistence !== undefined) { throw new HiveDriverError( - 'SEA backend: `persistence` (custom OAuth token store) is not yet wired through ' + + 'kernel backend: `persistence` (custom OAuth token store) is not yet wired through ' + 'to the kernel — requires `AuthConfig::External` plumbing. ' + 'Today the kernel auto-persists U2M tokens to ' + '`~/.config/databricks-sql-kernel/oauth/` which works for the standard flow; ' + @@ -409,17 +411,17 @@ export function buildSeaConnectionOptions(options: ConnectionOptions): SeaNative // M2M. if (typeof oauth.oauthClientId !== 'string' || isBlankOrReserved(oauth.oauthClientId)) { throw new AuthenticationError( - 'SEA backend: `oauthClientId` is required (non-empty, non-whitespace) for OAuth M2M.', + 'kernel backend: `oauthClientId` is required (non-empty, non-whitespace) for OAuth M2M.', ); } if (typeof oauth.oauthClientSecret !== 'string' || isBlankOrReserved(oauth.oauthClientSecret)) { throw new AuthenticationError( - 'SEA backend: `oauthClientSecret` must be a non-empty non-whitespace string for OAuth M2M.', + 'kernel backend: `oauthClientSecret` must be a non-empty non-whitespace string for OAuth M2M.', ); } if (oauth.persistence !== undefined) { throw new HiveDriverError( - 'SEA backend: `persistence` is not supported on OAuth M2M ' + + 'kernel backend: `persistence` is not supported on OAuth M2M ' + '(M2M tokens have no refresh token; the kernel re-issues on expiry).', ); } @@ -432,8 +434,8 @@ export function buildSeaConnectionOptions(options: ConnectionOptions): SeaNative } throw new HiveDriverError( - `SEA backend: unsupported auth mode '${authType}'. ` + - "Supported modes on the SEA backend today: 'access-token' (PAT) and 'databricks-oauth' " + + `kernel backend: unsupported auth mode '${authType}'. ` + + "Supported modes on the kernel backend today: 'access-token' (PAT) and 'databricks-oauth' " + '(M2M with oauthClientId+oauthClientSecret, or U2M with neither).', ); } diff --git a/lib/sea/SeaBackend.ts b/lib/kernel/KernelBackend.ts similarity index 74% rename from lib/sea/SeaBackend.ts rename to lib/kernel/KernelBackend.ts index 6f1bd5f0..cbffb8b8 100644 --- a/lib/sea/SeaBackend.ts +++ b/lib/kernel/KernelBackend.ts @@ -19,31 +19,31 @@ import { ConnectionOptions, OpenSessionRequest } from '../contracts/IDBSQLClient import { InternalConnectionOptions } from '../contracts/InternalConnectionOptions'; import { LogLevel } from '../contracts/IDBSQLLogger'; import HiveDriverError from '../errors/HiveDriverError'; -import { getSeaNative, SeaNativeBinding, SeaConnection } from './SeaNativeLoader'; -import { decodeNapiKernelError } from './SeaErrorMapping'; -import { buildSeaConnectionOptions, SeaNativeConnectionOptions } from './SeaAuth'; -import { installKernelLogBridge } from './SeaLogging'; -import SeaSessionBackend from './SeaSessionBackend'; +import { getKernelNative, KernelNativeBinding, KernelConnection } from './KernelNativeLoader'; +import { decodeNapiKernelError } from './KernelErrorMapping'; +import { buildKernelConnectionOptions, KernelNativeConnectionOptions } from './KernelAuth'; +import { installKernelLogBridge } from './KernelLogging'; +import KernelSessionBackend from './KernelSessionBackend'; -export interface SeaBackendOptions { +export interface KernelBackendOptions { /** - * Required. Provides the logger + config the SEA session/operation chain - * logs through. `DBSQLClient` supplies it via the SEA seam - * (`new SeaBackend({ context: this })`); unit tests pass a stub. Kept + * Required. Provides the logger + config the kernel session/operation chain + * logs through. `DBSQLClient` supplies it via the kernel seam + * (`new KernelBackend({ context: this })`); unit tests pass a stub. Kept * mandatory (rather than an `as IClientContext` downcast of `undefined`) * so a missing context is a compile error, not a latent runtime NPE. */ context: IClientContext; /** * Optional injection seam for unit tests. When provided, replaces the - * default `getSeaNative()` call so tests can swap in a mock napi + * default `getKernelNative()` call so tests can swap in a mock napi * binding without loading the `.node` artifact. */ - nativeBinding?: SeaNativeBinding; + nativeBinding?: KernelNativeBinding; } /** - * SEA-backed implementation of `IBackend`. + * kernel-backed implementation of `IBackend`. * * **M0 dispatch model:** the napi binding's `openSession()` already * builds a kernel `Session` from PAT + hostname + httpPath, so there is @@ -51,41 +51,41 @@ export interface SeaBackendOptions { * captures the `ConnectionOptions` and validates that PAT auth is in * use. The actual session open happens inside `openSession()`. * - * **Auth validation:** delegates to `buildSeaConnectionOptions` from - * `SeaAuth`, which mirrors the existing DBSQLClient validation pattern + * **Auth validation:** delegates to `buildKernelConnectionOptions` from + * `KernelAuth`, which mirrors the existing DBSQLClient validation pattern * (slash-prepended httpPath, AuthenticationError on missing token or * blank OAuth credentials, HiveDriverError on unsupported authType / * Azure-direct / ambiguous credential combinations). M2M and U2M - * routing key off `oauthClientId` presence; see SeaAuth.ts. + * routing key off `oauthClientId` presence; see KernelAuth.ts. * * **Why we don't use IClientContext's connectionProvider here:** that * provider is the Thrift HTTP transport. The kernel owns its own * reqwest+rustls stack inside the native binding, so there is no - * NodeJS-level connection state to manage on the SEA path. The + * NodeJS-level connection state to manage on the kernel path. The * `IClientContext` is still useful for logger + config access. */ -export default class SeaBackend implements IBackend { +export default class KernelBackend implements IBackend { private readonly context: IClientContext; - private readonly binding: SeaNativeBinding; + private readonly binding: KernelNativeBinding; - private nativeOptions?: SeaNativeConnectionOptions; + private nativeOptions?: KernelNativeConnectionOptions; // Drops the kernel-log level-change listener on close. No-op until connect() // installs the bridge (and a no-op closure if the logger/binding can't // retarget at runtime). private kernelLogUnsubscribe: () => void = () => {}; - constructor(options: SeaBackendOptions) { + constructor(options: KernelBackendOptions) { this.context = options.context; - this.binding = options.nativeBinding ?? getSeaNative(); + this.binding = options.nativeBinding ?? getKernelNative(); } public async connect(options: ConnectionOptions): Promise { // Validate PAT auth + capture the napi-binding option shape. // Any non-PAT mode (or a missing/empty token) throws here, before // we ever touch the native binding. - this.nativeOptions = buildSeaConnectionOptions(options); + this.nativeOptions = buildKernelConnectionOptions(options); // Bridge the Rust kernel's `tracing` logs into the SAME `DBSQLLogger` the // driver logs through, so logs from all three layers (driver, napi shim, @@ -108,7 +108,7 @@ export default class SeaBackend implements IBackend { .getLogger() .log( LogLevel.warn, - 'SEA: `customCaCert` is set but `checkServerCertificate: false` disables certificate ' + + 'kernel: `customCaCert` is set but `checkServerCertificate: false` disables certificate ' + 'verification entirely — the custom CA is not used. Set `checkServerCertificate: true` to use it.', ); } @@ -116,7 +116,7 @@ export default class SeaBackend implements IBackend { public async openSession(request: OpenSessionRequest): Promise { if (!this.nativeOptions) { - throw new HiveDriverError('SeaBackend: not connected. Call connect() first.'); + throw new HiveDriverError('KernelBackend: not connected. Call connect() first.'); } // Fold session-level defaults from the OpenSessionRequest into the @@ -128,7 +128,7 @@ export default class SeaBackend implements IBackend { // 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 }; + const sessionOptions: KernelNativeConnectionOptions = { ...this.nativeOptions }; if (request.initialCatalog !== undefined) { sessionOptions.catalog = request.initialCatalog; } @@ -139,22 +139,22 @@ export default class SeaBackend implements IBackend { sessionOptions.sessionConf = { ...request.configuration }; } - let nativeConnection: SeaConnection; + let nativeConnection: KernelConnection; try { - // `SeaNativeConnectionOptions.authMode` is a string-literal union + // `KernelNativeConnectionOptions.authMode` is a string-literal union // ('Pat' | 'OAuthM2m' | 'OAuthU2m') — deliberately not the binding's - // `const enum AuthMode` (see SeaAuth's note on why a const-enum import + // `const enum AuthMode` (see KernelAuth's note on why a const-enum import // is avoided). The literal values are byte-identical to the enum's, so // the only divergence is TS's const-enum strictness; cast to the // binding's parameter type at this single boundary. nativeConnection = (await this.binding.openSession( - sessionOptions as unknown as Parameters[0], - )) as SeaConnection; + sessionOptions as unknown as Parameters[0], + )) as KernelConnection; } catch (err) { throw decodeNapiKernelError(err); } - return new SeaSessionBackend({ + return new KernelSessionBackend({ connection: nativeConnection!, context: this.context, id: nativeConnection!.sessionId, @@ -162,7 +162,7 @@ export default class SeaBackend implements IBackend { } public async close(): Promise { - // No backend-level resources to release — each `SeaSessionBackend` + // No backend-level resources to release — each `KernelSessionBackend` // owns its own napi `Connection` lifecycle. this.nativeOptions = undefined; diff --git a/lib/sea/SeaErrorMapping.ts b/lib/kernel/KernelErrorMapping.ts similarity index 97% rename from lib/sea/SeaErrorMapping.ts rename to lib/kernel/KernelErrorMapping.ts index 1dcd693a..b955f539 100644 --- a/lib/sea/SeaErrorMapping.ts +++ b/lib/kernel/KernelErrorMapping.ts @@ -7,7 +7,7 @@ import ParameterError from '../errors/ParameterError'; * Sentinel prefix the napi binding's `napi_err_from_kernel` puts on * `Error.message` when the underlying failure was a structured kernel * `Error` rather than a plain napi `InvalidArg` from binding-side - * validation. Defined here (and in `native/sea/src/error.rs:44`) — the + * validation. Defined here (and in `native/kernel/src/error.rs:44`) — the * two MUST stay in lockstep. */ const ERROR_SENTINEL = '__databricks_error__:'; @@ -53,7 +53,7 @@ export type KernelErrorCode = /** * Optional metadata fields the kernel may attach via the - * `__databricks_error__:` envelope (per `native/sea/src/error.rs:50-89`). + * `__databricks_error__:` envelope (per `native/kernel/src/error.rs:50-89`). * * `errorCode` is namespaced under `kernelMetadata` rather than placed at * the top level because `OperationStateError` already declares a top-level @@ -71,7 +71,7 @@ export interface KernelMetadata { } /** - * An `Error` carrying optional SEA-side kernel context. `sqlState` is + * An `Error` carrying optional kernel-side kernel context. `sqlState` is * exposed at the top level (no collision in the existing driver error * tree); the remaining envelope fields live under a `kernelMetadata` * namespace to avoid clobbering pre-existing `errorCode` semantics on @@ -235,7 +235,7 @@ function buildKernelMetadata(parsed: Record): KernelMetadata { */ export function decodeNapiKernelError(err: unknown): Error { if (!(err instanceof Error)) { - return new HiveDriverError(typeof err === 'string' ? err : 'SEA backend: unknown error'); + return new HiveDriverError(typeof err === 'string' ? err : 'kernel backend: unknown error'); } const { message } = err; diff --git a/lib/sea/SeaInputValidation.ts b/lib/kernel/KernelInputValidation.ts similarity index 96% rename from lib/sea/SeaInputValidation.ts rename to lib/kernel/KernelInputValidation.ts index d368ccea..a397542b 100644 --- a/lib/sea/SeaInputValidation.ts +++ b/lib/kernel/KernelInputValidation.ts @@ -20,7 +20,7 @@ import ParameterError from '../errors/ParameterError'; * Reject a parameter value that cannot be bound as a scalar. Arrays and plain * objects stringify to garbage (e.g. `[1,2,3]` → `"1,2,3"`) that the server * fails to coerce — on the Thrift path the operation never returns to - * FINISHED (a DoS hazard), and on SEA it surfaces an opaque server error. We + * FINISHED (a DoS hazard), and on kernel it surfaces an opaque server error. We * fail fast at bind time instead, mirroring the kernel's compound-type * rejection. `DBSQLParameter`, `Int64`, `Date`, and JS primitives are allowed. * diff --git a/lib/sea/SeaLogging.ts b/lib/kernel/KernelLogging.ts similarity index 91% rename from lib/sea/SeaLogging.ts rename to lib/kernel/KernelLogging.ts index 653dae5b..6de12a59 100644 --- a/lib/sea/SeaLogging.ts +++ b/lib/kernel/KernelLogging.ts @@ -30,7 +30,7 @@ */ import IDBSQLLogger, { LogLevel } from '../contracts/IDBSQLLogger'; -import { SeaNativeBinding, SeaNativeLogRecord } from './SeaNativeLoader'; +import { KernelNativeBinding, KernelNativeLogRecord } from './KernelNativeLoader'; /** * Map a kernel level string (`error`/`warn`/`info`/`debug`/`trace`) onto the @@ -70,7 +70,7 @@ export function logLevelToKernelLevel(level: LogLevel): string { * origin so kernel lines are distinguishable from driver lines in a shared * sink/file. */ -export function formatKernelLine(record: SeaNativeLogRecord): string { +export function formatKernelLine(record: KernelNativeLogRecord): string { return `[kernel ${record.target}] ${record.message}`; } @@ -96,17 +96,21 @@ export function formatKernelLine(record: SeaNativeLogRecord): string { * rather than a hard failure — logging is advisory. * * Returns an **unsubscribe** function the caller must invoke on teardown - * (`SeaBackend.close()`) to drop the level-change listener; it is a safe no-op + * (`KernelBackend.close()`) to drop the level-change listener; it is a safe no-op * when nothing was subscribed. */ -export function installKernelLogBridge(binding: SeaNativeBinding, logger: IDBSQLLogger, level: LogLevel): () => void { +export function installKernelLogBridge( + binding: KernelNativeBinding, + logger: IDBSQLLogger, + level: LogLevel, +): () => void { // Defensive: a stale/older binding without the bridge export must not break // connect() — logging is non-critical. if (typeof binding.initKernelLogging !== 'function') { return () => {}; } - const callback = (err: Error | null, records: Array): void => { + const callback = (err: Error | null, records: Array): void => { if (err || !records) { return; } diff --git a/lib/sea/SeaNativeLoader.ts b/lib/kernel/KernelNativeLoader.ts similarity index 75% rename from lib/sea/SeaNativeLoader.ts rename to lib/kernel/KernelNativeLoader.ts index e7be5e9b..0b98d156 100644 --- a/lib/sea/SeaNativeLoader.ts +++ b/lib/kernel/KernelNativeLoader.ts @@ -19,12 +19,12 @@ * `.node` artifact ships via per-platform optional dependencies * (`@databricks/databricks-sql-kernel-`), so its absence must not crash * a Thrift-only consumer of the driver. Callers that actually need - * SEA construct a {@link SeaNativeLoader} (or use the process-global - * {@link getSeaNative}) which throws a structured error if the binding + * kernel construct a {@link KernelNativeLoader} (or use the process-global + * {@link getKernelNative}) which throws a structured error if the binding * could not be loaded. * * M0 publishes a single triple (`linux-x64-gnu`); see - * `native/sea/README.md` for the supported-platform policy. + * `native/kernel/README.md` for the supported-platform policy. */ import type { @@ -40,18 +40,18 @@ import type { AsyncResultHandle as NativeAsyncResultHandle, CancellableExecution as NativeCancellableExecution, LogRecord as NativeLogRecord, -} from '../../native/sea'; +} from '../../native/kernel'; -// SEA-prefixed re-exports. The kernel-generated `.d.ts` keeps the +// kernel-prefixed re-exports. The kernel-generated `.d.ts` keeps the // napi-rs default names (`ConnectionOptions`, `ArrowBatch`, …); we // disambiguate on the TS-wrapper side so these never collide with the // Thrift-side `ConnectionOptions` (lib/contracts/IDBSQLClient.ts) or // `ArrowBatch` (lib/result/utils.ts) when imported elsewhere. -export type SeaConnectionOptions = NativeConnectionOptions; -export type SeaArrowBatch = NativeArrowBatch; -export type SeaArrowSchema = NativeArrowSchema; -export type SeaConnection = NativeConnection; -export type SeaStatement = NativeStatement; +export type KernelConnectionOptions = NativeConnectionOptions; +export type KernelArrowBatch = NativeArrowBatch; +export type KernelArrowSchema = NativeArrowSchema; +export type KernelConnection = NativeConnection; +export type KernelStatement = NativeStatement; // Per-statement execution options and bound-parameter inputs are kernel // concerns: the napi binding generates the canonical shapes (`positionalParams` @@ -59,17 +59,17 @@ export type SeaStatement = NativeStatement; // `rowLimit`, `statementConf`, `queryTags`). We re-export // rather than re-declare so the driver-side param codec can never drift from // the kernel contract. -export type SeaNativeExecuteOptions = NativeExecuteOptions; -export type SeaNativeTypedValueInput = NativeTypedValueInput; -export type SeaNativeNamedTypedValueInput = NativeNamedTypedValueInput; +export type KernelNativeExecuteOptions = NativeExecuteOptions; +export type KernelNativeTypedValueInput = NativeTypedValueInput; +export type KernelNativeNamedTypedValueInput = NativeNamedTypedValueInput; // Async-submit surface: `Connection.submitStatement` returns an // `AsyncStatement` (status / awaitResult / cancel / close); `awaitResult` // yields an `AsyncResultHandle` whose `fetchNextBatch` / `schema` match the // blocking `Statement`'s fetch surface, so the results pipeline consumes // either interchangeably. -export type SeaNativeAsyncStatement = NativeAsyncStatement; -export type SeaNativeAsyncResultHandle = NativeAsyncResultHandle; +export type KernelNativeAsyncStatement = NativeAsyncStatement; +export type KernelNativeAsyncResultHandle = NativeAsyncResultHandle; // Cancellable sync-execute surface: `Connection.executeStatementCancellable` // returns a `CancellableExecution` that captures a detached StatementCanceller @@ -77,12 +77,12 @@ export type SeaNativeAsyncResultHandle = NativeAsyncResultHandle; // interrupts a still-running query mid-compute. `result()` drives the blocking // execute and resolves to the same terminal `Statement` `executeStatement` // returns. -export type SeaNativeCancellableExecution = NativeCancellableExecution; +export type KernelNativeCancellableExecution = NativeCancellableExecution; -// One kernel log event forwarded over the napi log bridge (see SeaLogging.ts): +// One kernel log event forwarded over the napi log bridge (see KernelLogging.ts): // `{ level, target, message }`. Re-exported so the bridge can name the shape // without re-declaring it (stays in lock-step with the kernel contract). -export type SeaNativeLogRecord = NativeLogRecord; +export type KernelNativeLogRecord = NativeLogRecord; /** * The full native binding surface, derived from the generated module @@ -90,7 +90,7 @@ export type SeaNativeLogRecord = NativeLogRecord; * adds or renames a free function / class, this type follows * automatically and `defaultRequire`'s cast stays correct. */ -export type SeaNativeBinding = typeof import('../../native/sea'); +export type KernelNativeBinding = typeof import('../../native/kernel'); const MIN_NODE_MAJOR = 18; @@ -111,31 +111,31 @@ function loadFailureHint(err: NodeJS.ErrnoException): string { // 404. Point at the README's supported-triple list instead. const installHint = 'Install the matching @databricks/databricks-sql-kernel-* optional dependency for your platform ' + - '(see native/sea/README.md for the supported triples; M0 ships linux-x64-gnu only).'; + '(see native/kernel/README.md for the supported triples; M0 ships linux-x64-gnu only).'; if (err.code === 'MODULE_NOT_FOUND') { - return `SEA native binding not installed for platform ${platform} on Node ${process.version}. ${installHint}`; + return `kernel native binding not installed for platform ${platform} on Node ${process.version}. ${installHint}`; } if (err.code === 'ERR_DLOPEN_FAILED') { // Surface the underlying dlerror string (e.g. `GLIBC_2.32 not found`) // plus concrete remediation — without it the cause is invisible. return ( - `SEA native binding present but failed to dlopen on platform ${platform} / Node ${process.version}: ` + + `kernel native binding present but failed to dlopen on platform ${platform} / Node ${process.version}: ` + `${err.message}. Common causes: glibc/musl mismatch (e.g. Alpine Linux — install the -musl variant), ` + `Node ABI mismatch (try \`rm -rf node_modules && npm install\`), or CPU-architecture mismatch. ` + `The binding requires Node >=${MIN_NODE_MAJOR}.` ); } - return `SEA native binding failed to load on platform ${platform} / Node ${process.version}: ${err.message}`; + return `kernel native binding failed to load on platform ${platform} / Node ${process.version}: ${err.message}`; } /** - * Default loader: resolves `native/sea/index.js` (the napi-rs router), + * Default loader: resolves `native/kernel/index.js` (the napi-rs router), * which selects the per-platform `.node`. `.js` is omitted so eslint's * `import/extensions` rule accepts the call. */ -function defaultRequire(): SeaNativeBinding { +function defaultRequire(): KernelNativeBinding { // eslint-disable-next-line @typescript-eslint/no-var-requires, global-require - return require('../../native/sea') as SeaNativeBinding; + return require('../../native/kernel') as KernelNativeBinding; } /** @@ -143,7 +143,7 @@ function defaultRequire(): SeaNativeBinding { * Catches kernel-side renames at load time rather than letting them * surface as `undefined is not a function` deep in a call path. */ -function assertBindingShape(binding: SeaNativeBinding): void { +function assertBindingShape(binding: KernelNativeBinding): void { const missing: string[] = []; if (typeof binding.version !== 'function') missing.push('version'); if (typeof binding.openSession !== 'function') missing.push('openSession'); @@ -158,21 +158,21 @@ function assertBindingShape(binding: SeaNativeBinding): void { if (typeof binding.CancellableExecution !== 'function') missing.push('CancellableExecution'); if (missing.length > 0) { throw new Error( - `SEA native binding loaded but is missing expected export(s): ${missing.join(', ')}. ` + + `kernel native binding loaded but is missing expected export(s): ${missing.join(', ')}. ` + `The kernel-generated binding and the JS loader are out of sync.`, ); } } /** - * Loads and caches the SEA native binding. Exposed as a class with an - * injectable `load` seam so consumers (e.g. `SeaBackend`) can be unit + * Loads and caches the kernel native binding. Exposed as a class with an + * injectable `load` seam so consumers (e.g. `KernelBackend`) can be unit * tested with a stub binding instead of requiring a real `.node` on the * test machine. Most production code uses the process-global default - * via {@link getSeaNative} / {@link tryGetSeaNative}. + * via {@link getKernelNative} / {@link tryGetKernelNative}. */ -export class SeaNativeLoader { - private cached: SeaNativeBinding | null | undefined; +export class KernelNativeLoader { + private cached: KernelNativeBinding | null | undefined; private cachedError: Error | undefined; @@ -184,17 +184,17 @@ export class SeaNativeLoader { * runner's actual Node version (the matrix spans 14–20). */ constructor( - private readonly load: () => SeaNativeBinding = defaultRequire, + private readonly load: () => KernelNativeBinding = defaultRequire, private readonly nodeMajor: () => number = detectNodeMajor, ) {} - private tryLoad(): SeaNativeBinding | undefined { + private tryLoad(): KernelNativeBinding | undefined { const nodeMajor = this.nodeMajor(); // Fail closed: if we cannot determine the Node major (NaN) or it is // below the floor, refuse the load and fall back to Thrift. if (!Number.isFinite(nodeMajor) || nodeMajor < MIN_NODE_MAJOR) { this.cachedError = new Error( - `SEA native binding requires Node >=${MIN_NODE_MAJOR}; running Node ${process.version}. ` + + `kernel native binding requires Node >=${MIN_NODE_MAJOR}; running Node ${process.version}. ` + `Continue using the Thrift backend on this runtime.`, ); return undefined; @@ -211,7 +211,7 @@ export class SeaNativeLoader { // Shape-check failure or any other Error — preserve its message. this.cachedError = err; } else { - this.cachedError = new Error(`SEA native binding failed to load with non-standard error: ${String(err)}`); + this.cachedError = new Error(`kernel native binding failed to load with non-standard error: ${String(err)}`); } return undefined; } @@ -221,12 +221,12 @@ export class SeaNativeLoader { * Returns the loaded native binding. Throws a structured error if the * binding is unavailable on this platform / Node version. */ - get(): SeaNativeBinding { + get(): KernelNativeBinding { if (this.cached === undefined) { this.cached = this.tryLoad() ?? null; } if (this.cached === null) { - throw this.cachedError ?? new Error('SEA native binding unavailable'); + throw this.cachedError ?? new Error('kernel native binding unavailable'); } return this.cached; } @@ -234,9 +234,9 @@ export class SeaNativeLoader { /** * Returns the loaded binding or `undefined` if it could not be * loaded. Use this for capability-detection at startup; use - * {@link get} at the point where SEA is actually required. + * {@link get} at the point where kernel is actually required. */ - tryGet(): SeaNativeBinding | undefined { + tryGet(): KernelNativeBinding | undefined { if (this.cached === undefined) { this.cached = this.tryLoad() ?? null; } @@ -245,13 +245,13 @@ export class SeaNativeLoader { } // Process-global default instance + thin convenience wrappers. -const defaultLoader = new SeaNativeLoader(); +const defaultLoader = new KernelNativeLoader(); /** * Returns the loaded native binding from the process-global loader. * Throws a structured error if the binding is unavailable. */ -export function getSeaNative(): SeaNativeBinding { +export function getKernelNative(): KernelNativeBinding { return defaultLoader.get(); } @@ -259,6 +259,6 @@ export function getSeaNative(): SeaNativeBinding { * Returns the loaded binding from the process-global loader, or * `undefined` if it could not be loaded. */ -export function tryGetSeaNative(): SeaNativeBinding | undefined { +export function tryGetKernelNative(): KernelNativeBinding | undefined { return defaultLoader.tryGet(); } diff --git a/lib/sea/SeaOperationBackend.ts b/lib/kernel/KernelOperationBackend.ts similarity index 85% rename from lib/sea/SeaOperationBackend.ts rename to lib/kernel/KernelOperationBackend.ts index 7678b0a3..88dd8e01 100644 --- a/lib/sea/SeaOperationBackend.ts +++ b/lib/kernel/KernelOperationBackend.ts @@ -13,19 +13,19 @@ // limitations under the License. /** - * `IOperationBackend` implementation for the SEA path. + * `IOperationBackend` implementation for the kernel path. * * Combines: - * - **Fetch pipeline (from sea-results):** - * `napi.Statement.fetchNextBatch()` → `SeaResultsProvider` → + * - **Fetch pipeline (from kernel-results):** + * `napi.Statement.fetchNextBatch()` → `KernelResultsProvider` → * `ArrowResultConverter` (Phase 1 + Phase 2; reused unchanged) → * `ResultSlicer` (chunk-size normalisation; reused unchanged). The M0 * row shape is byte-identical to the thrift path for every M0 - * datatype (parity gate exercised by `tests/e2e/sea/results-e2e.test.ts`). + * datatype (parity gate exercised by `tests/e2e/kernel/results-e2e.test.ts`). * - * - **Lifecycle (from sea-operation):** `cancel()` / `close()` / + * - **Lifecycle (from kernel-operation):** `cancel()` / `close()` / * `finished()` (alias of `waitUntilReady`) delegate to the helpers - * in `SeaOperationLifecycle.ts`. The helpers handle idempotency, + * in `KernelOperationLifecycle.ts`. The helpers handle idempotency, * flag-set-before-await ordering (so cancel-mid-fetch propagates), * logging via `IClientContext`, and kernel-error mapping. * @@ -48,24 +48,24 @@ import HiveDriverError from '../errors/HiveDriverError'; import OperationStateError, { OperationStateErrorCode } from '../errors/OperationStateError'; import ArrowResultConverter from '../result/ArrowResultConverter'; import ResultSlicer from '../result/ResultSlicer'; -import SeaResultsProvider from './SeaResultsProvider'; -import { arrowSchemaToThriftSchema, decodeIpcSchema, patchIpcBytes } from './SeaArrowIpc'; -import { decodeNapiKernelError } from './SeaErrorMapping'; +import KernelResultsProvider from './KernelResultsProvider'; +import { arrowSchemaToThriftSchema, decodeIpcSchema, patchIpcBytes } from './KernelArrowIpc'; +import { decodeNapiKernelError } from './KernelErrorMapping'; import { - SeaStatement, - SeaNativeAsyncStatement, - SeaNativeAsyncResultHandle, - SeaNativeCancellableExecution, -} from './SeaNativeLoader'; + KernelStatement, + KernelNativeAsyncStatement, + KernelNativeAsyncResultHandle, + KernelNativeCancellableExecution, +} from './KernelNativeLoader'; import { - SeaStatementHandle, - SeaOperationLifecycleState, + KernelStatementHandle, + KernelOperationLifecycleState, createLifecycleState, - seaCancel, - seaClose, - seaFinished, + kernelCancel, + kernelClose, + kernelFinished, failIfNotActive, -} from './SeaOperationLifecycle'; +} from './KernelOperationLifecycle'; /** * Structural union of the lifecycle surface (cancel/close) and the @@ -74,16 +74,16 @@ import { * cancel/close half — fetch methods are accessed lazily and the * lifecycle tests never reach that path. */ -export type SeaOperationStatement = SeaStatementHandle & Partial; +export type KernelOperationStatement = KernelStatementHandle & Partial; /** * The fetch surface shared by the blocking metadata `Statement` and the async * query path's `AsyncResultHandle` (from `awaitResult()`): both expose * `fetchNextBatch()` + a synchronous `schema()`, so the results pipeline - * (`SeaResultsProvider` → `ArrowResultConverter` → `ResultSlicer`) consumes + * (`KernelResultsProvider` → `ArrowResultConverter` → `ResultSlicer`) consumes * either interchangeably. */ -type SeaFetchHandle = Pick; +type KernelFetchHandle = Pick; /** Poll cadence for the async `status()` loop — matches the Thrift backend's 100ms. */ const STATUS_POLL_INTERVAL_MS = 100; @@ -116,15 +116,15 @@ function statusStringToOperationState(state: string): OperationState { } /** - * Constructor options for `SeaOperationBackend`. Exactly one of + * Constructor options for `KernelOperationBackend`. Exactly one of * `asyncStatement` (query path — `Connection.submitStatement`) or `statement` * (metadata path — `Connection.list*` / `get*`, already terminal) must be set. */ -export interface SeaOperationBackendOptions { +export interface KernelOperationBackendOptions { /** The pending napi `AsyncStatement` from `Connection.submitStatement(...)`. */ - asyncStatement?: SeaNativeAsyncStatement; + asyncStatement?: KernelNativeAsyncStatement; /** The terminal napi `Statement` from a metadata call. */ - statement?: SeaOperationStatement; + statement?: KernelOperationStatement; /** * The pending napi `CancellableExecution` from * `Connection.executeStatementCancellable(...)` — the sync (`runAsync: false`) @@ -133,7 +133,7 @@ export interface SeaOperationBackendOptions { * interrupts a still-running `result()` mid-COMPUTE. Exactly one of * `asyncStatement`, `statement`, or `cancellableExecution` must be set. */ - cancellableExecution?: SeaNativeCancellableExecution; + cancellableExecution?: KernelNativeCancellableExecution; context: IClientContext; /** * Optional override for `id`. Defaults to the napi statement-id when the @@ -142,35 +142,35 @@ export interface SeaOperationBackendOptions { id?: string; } -export default class SeaOperationBackend implements IOperationBackend { +export default class KernelOperationBackend implements IOperationBackend { // Async query path: pending async statement we poll to terminal. Undefined on // the metadata / sync-execute paths. - private readonly asyncStatement?: SeaNativeAsyncStatement; + private readonly asyncStatement?: KernelNativeAsyncStatement; // Sync query path (`runAsync: false`): pending cancellable execution whose // `result()` drives the blocking `execute()` to a terminal `Statement`. // Undefined on the async / metadata paths. - private readonly cancellableExecution?: SeaNativeCancellableExecution; + private readonly cancellableExecution?: KernelNativeCancellableExecution; // Metadata path: terminal statement. Also the resolved fetch handle on the // sync-execute path once `cancellableExecution.result()` settles. - private blockingStatement?: SeaOperationStatement; + private blockingStatement?: KernelOperationStatement; // The cancel/close surface — whichever handle backs this operation. Both // `AsyncStatement` and `Statement` expose `cancel()` / `close()`; the // sync-execute path uses a composite that routes `cancel()` to the // cancellable execution (mid-compute) and `close()` to the resolved statement. - private readonly lifecycleHandle: SeaStatementHandle; + private readonly lifecycleHandle: KernelStatementHandle; private readonly context: IClientContext; private readonly _id: string; - private readonly lifecycle: SeaOperationLifecycleState = createLifecycleState(); + private readonly lifecycle: KernelOperationLifecycleState = createLifecycleState(); private resultSlicer?: ResultSlicer; - private resultsProvider?: SeaResultsProvider; + private resultsProvider?: KernelResultsProvider; private metadata?: ResultMetadata; @@ -179,9 +179,9 @@ export default class SeaOperationBackend implements IOperationBackend { // Memoised fetch handle: on the async path it is `awaitResult()`'s result // (resolved once the statement is terminal); on the metadata path it is the // already-terminal statement. Drives both fetch and result-metadata. - private fetchHandlePromise?: Promise; + private fetchHandlePromise?: Promise; - constructor({ asyncStatement, statement, cancellableExecution, context, id }: SeaOperationBackendOptions) { + constructor({ asyncStatement, statement, cancellableExecution, context, id }: KernelOperationBackendOptions) { // Exactly one of the three handle kinds must be supplied. const providedCount = (asyncStatement !== undefined ? 1 : 0) + @@ -189,7 +189,7 @@ export default class SeaOperationBackend implements IOperationBackend { (cancellableExecution !== undefined ? 1 : 0); if (providedCount !== 1) { throw new HiveDriverError( - 'SeaOperationBackend: exactly one of `asyncStatement`, `statement`, or `cancellableExecution` must be provided', + 'KernelOperationBackend: exactly one of `asyncStatement`, `statement`, or `cancellableExecution` must be provided', ); } this.asyncStatement = asyncStatement; @@ -208,7 +208,7 @@ export default class SeaOperationBackend implements IOperationBackend { cancel: () => cancellableExecution.cancel(), close: () => (this.blockingStatement ? this.blockingStatement.close() : cancellableExecution.cancel()), } - : ((asyncStatement ?? statement) as SeaStatementHandle); + : ((asyncStatement ?? statement) as KernelStatementHandle); this.context = context; this._id = id ?? asyncStatement?.statementId ?? statement?.statementId ?? cancellableExecution?.statementId ?? uuidv4(); @@ -227,8 +227,8 @@ export default class SeaOperationBackend implements IOperationBackend { } public hasResultSet(): boolean { - // M0 only routes through SeaOperationBackend for executeStatement - // calls. DDL/DML without a result set is not exercised through SEA + // M0 only routes through KernelOperationBackend for executeStatement + // calls. DDL/DML without a result set is not exercised through kernel // for M0; the napi Statement still produces a schema (empty) in // that case, which the converter renders as zero rows. Reporting // `true` keeps the facade's fetch path enabled for M0 parity. @@ -236,7 +236,7 @@ export default class SeaOperationBackend implements IOperationBackend { } // --------------------------------------------------------------------------- - // Fetch / metadata (owned by the sea-results pipeline). + // Fetch / metadata (owned by the kernel-results pipeline). // --------------------------------------------------------------------------- public async fetchChunk({ @@ -270,18 +270,18 @@ export default class SeaOperationBackend implements IOperationBackend { // reclaims it promptly — best-effort, so a close failure never masks the // original fetch error — then surface a typed kernel error. // - // If close() ALSO fails, seaClose has reset isClosed back to false and + // If close() ALSO fails, kernelClose has reset isClosed back to false and // the kernel-side statement handle is now leaked (the stream is already // wedged, so nothing downstream forces another close). We still don't // mask the original fetch error, but log the close failure at warn so // the leak is diagnosable rather than completely invisible. - await seaClose(this.lifecycle, this.lifecycleHandle, this.context, this._id).catch((closeErr) => { + await kernelClose(this.lifecycle, this.lifecycleHandle, this.context, this._id).catch((closeErr) => { const cause = closeErr instanceof Error ? closeErr.message : String(closeErr); this.context .getLogger() .log( LogLevel.warn, - `SEA fetch-error cleanup: close() failed for operation ${this._id}; the server-side ` + + `kernel fetch-error cleanup: close() failed for operation ${this._id}; the server-side ` + `statement may leak until the session is closed. Cause: ${cause}`, ); }); @@ -309,7 +309,7 @@ export default class SeaOperationBackend implements IOperationBackend { // `getFetchHandle()` once the statement is terminal). const handle = await this.getFetchHandle(); if (!handle.schema) { - throw new HiveDriverError('SeaOperationBackend: schema() is not available on this handle'); + throw new HiveDriverError('KernelOperationBackend: schema() is not available on this handle'); } // `schema()` is a synchronous napi getter (returns `ArrowSchema`, not a // Promise) — no `await` needed. @@ -320,7 +320,7 @@ export default class SeaOperationBackend implements IOperationBackend { const thriftSchema: TTableSchema = arrowSchemaToThriftSchema(arrowSchema); const meta: ResultMetadata = { schema: thriftSchema, - // SEA inline + CloudFetch both surface to JS as Arrow batches; + // kernel inline + CloudFetch both surface to JS as Arrow batches; // both flow through the same Arrow result converter. resultFormat: ResultFormat.ArrowBased, lz4Compressed: false, @@ -342,7 +342,7 @@ export default class SeaOperationBackend implements IOperationBackend { } // --------------------------------------------------------------------------- - // Status / lifecycle (owned by the sea-operation lifecycle helpers). + // Status / lifecycle (owned by the kernel-operation lifecycle helpers). // --------------------------------------------------------------------------- public async status(_progress: boolean): Promise { @@ -380,17 +380,17 @@ export default class SeaOperationBackend implements IOperationBackend { return this.waitUntilReadyCancellable(options); } // Metadata path: the kernel statement has already resolved, so there is - // nothing to poll. seaFinished fires the progress callback once with a + // nothing to poll. kernelFinished fires the progress callback once with a // synthesised completion tick, matching the Thrift path's final tick. - return seaFinished(this.lifecycle, options); + return kernelFinished(this.lifecycle, options); } public async cancel(): Promise { - return seaCancel(this.lifecycle, this.lifecycleHandle, this.context, this._id); + return kernelCancel(this.lifecycle, this.lifecycleHandle, this.context, this._id); } public async close(): Promise { - return seaClose(this.lifecycle, this.lifecycleHandle, this.context, this._id); + return kernelClose(this.lifecycle, this.lifecycleHandle, this.context, this._id); } // --------------------------------------------------------------------------- @@ -471,7 +471,7 @@ export default class SeaOperationBackend implements IOperationBackend { } // Still Pending/Running — sleep before the next poll. (There is no - // client-side query-timeout deadline: `queryTimeout` is a no-op on SEA.) + // client-side query-timeout deadline: `queryTimeout` is a no-op on kernel.) // eslint-disable-next-line no-await-in-loop await delay(STATUS_POLL_INTERVAL_MS); } @@ -518,7 +518,7 @@ export default class SeaOperationBackend implements IOperationBackend { } catch (err) { throw decodeNapiKernelError(err); } - throw new HiveDriverError(`SEA operation ${this._id} reported Failed but produced a result.`); + throw new HiveDriverError(`kernel operation ${this._id} reported Failed but produced a result.`); } /** @@ -529,13 +529,13 @@ export default class SeaOperationBackend implements IOperationBackend { * original error; warn-logs a close failure so the leak is diagnosable. */ private async bestEffortClose(): Promise { - await seaClose(this.lifecycle, this.lifecycleHandle, this.context, this._id).catch((closeErr) => { + await kernelClose(this.lifecycle, this.lifecycleHandle, this.context, this._id).catch((closeErr) => { const cause = closeErr instanceof Error ? closeErr.message : String(closeErr); this.context .getLogger() .log( LogLevel.warn, - `SEA poll-loop cleanup: close() failed for operation ${this._id}; the server-side ` + + `kernel poll-loop cleanup: close() failed for operation ${this._id}; the server-side ` + `statement may leak until the session is closed. Cause: ${cause}`, ); }); @@ -545,12 +545,12 @@ export default class SeaOperationBackend implements IOperationBackend { * Resolve (and memoise) the fetch handle: `awaitResult()`'s `AsyncResultHandle` * on the query path, or the already-terminal `Statement` on the metadata path. */ - private getFetchHandle(): Promise { + private getFetchHandle(): Promise { if (!this.fetchHandlePromise) { if (this.asyncStatement) { this.fetchHandlePromise = this.asyncStatement.awaitResult().catch((err) => { throw decodeNapiKernelError(err); - }) as Promise; + }) as Promise; } else if (this.cancellableExecution) { // Sync (`runAsync: false`) path: drive the blocking `result()` to the // terminal `Statement`. Store it on `blockingStatement` so `close()` can @@ -558,7 +558,7 @@ export default class SeaOperationBackend implements IOperationBackend { this.fetchHandlePromise = this.cancellableExecution .result() .then((stmt) => { - this.blockingStatement = stmt as unknown as SeaOperationStatement; + this.blockingStatement = stmt as unknown as KernelOperationStatement; // Log the now-known server statement id (NOT surfaced via `id`, // which must stay stable for telemetry correlation) so a sync op is // correlatable to server/kernel logs by its client operation id. @@ -566,9 +566,9 @@ export default class SeaOperationBackend implements IOperationBackend { if (serverId && serverId !== this._id) { this.context .getLogger() - .log(LogLevel.debug, `SEA operation ${this._id} resolved to server statement_id ${serverId}`); + .log(LogLevel.debug, `kernel operation ${this._id} resolved to server statement_id ${serverId}`); } - return stmt as unknown as SeaFetchHandle; + return stmt as unknown as KernelFetchHandle; }) .catch((err) => { const mapped = decodeNapiKernelError(err); @@ -586,9 +586,11 @@ export default class SeaOperationBackend implements IOperationBackend { } else { const stmt = this.blockingStatement!; if (!stmt.fetchNextBatch) { - throw new HiveDriverError('SeaOperationBackend: statement.fetchNextBatch() is not available on this handle'); + throw new HiveDriverError( + 'KernelOperationBackend: statement.fetchNextBatch() is not available on this handle', + ); } - this.fetchHandlePromise = Promise.resolve(stmt as unknown as SeaFetchHandle); + this.fetchHandlePromise = Promise.resolve(stmt as unknown as KernelFetchHandle); } } return this.fetchHandlePromise; @@ -600,9 +602,9 @@ export default class SeaOperationBackend implements IOperationBackend { } const metadata = await this.getResultMetadata(); const handle = await this.getFetchHandle(); - // SeaResultsProvider consumes only `fetchNextBatch`; both the async result + // KernelResultsProvider consumes only `fetchNextBatch`; both the async result // handle and the blocking statement satisfy that surface. - this.resultsProvider = new SeaResultsProvider(handle as unknown as SeaStatement); + this.resultsProvider = new KernelResultsProvider(handle as unknown as KernelStatement); const converter = new ArrowResultConverter(this.context, this.resultsProvider, metadata); this.resultSlicer = new ResultSlicer(this.context, converter); return this.resultSlicer; diff --git a/lib/sea/SeaOperationLifecycle.ts b/lib/kernel/KernelOperationLifecycle.ts similarity index 85% rename from lib/sea/SeaOperationLifecycle.ts rename to lib/kernel/KernelOperationLifecycle.ts index d8b6b2c9..ada806f3 100644 --- a/lib/sea/SeaOperationLifecycle.ts +++ b/lib/kernel/KernelOperationLifecycle.ts @@ -13,23 +13,23 @@ // limitations under the License. /** - * SEA operation lifecycle helpers (M0). + * kernel operation lifecycle helpers (M0). * * The three methods exposed here (`cancel`, `close`, `finished`) are - * standalone functions that the `SeaOperationBackend` implementation + * standalone functions that the `KernelOperationBackend` implementation * delegates to. Keeping them in this dedicated file lets the parallel * impl-results work (which owns the fetch-* methods on - * `SeaOperationBackend`) land independently — at merge time it can + * `KernelOperationBackend`) land independently — at merge time it can * either import these helpers from here or inline them, with no * conflicts on the call sites. * * Mapping to the existing `DBSQLOperation` semantics: * - `cancel()` → ` driver.cancelOperation(...)` on Thrift today - * (`lib/DBSQLOperation.ts:241-259`). For SEA this is a one-shot + * (`lib/DBSQLOperation.ts:241-259`). For kernel this is a one-shot * forward to the napi `Statement.cancel()` which in turn calls * `ExecutedStatementHandle::cancel(&self).await` in the kernel. * - `close()` → `driver.closeOperation(...)` on Thrift today - * (`lib/DBSQLOperation.ts:265-284`). For SEA this is the napi + * (`lib/DBSQLOperation.ts:265-284`). For kernel this is the napi * `Statement.close()` which awaits the server-side delete. * - `finished({progress, callback})` → the 100ms polling loop in * `DBSQLOperation.waitUntilReady` today (`lib/DBSQLOperation.ts:337-391`). @@ -47,7 +47,7 @@ import Status from '../dto/Status'; import { OperationStatus, OperationState } from '../contracts/OperationStatus'; import { LogLevel } from '../contracts/IDBSQLLogger'; import IClientContext from '../contracts/IClientContext'; -import { decodeNapiKernelError } from './SeaErrorMapping'; +import { decodeNapiKernelError } from './KernelErrorMapping'; import OperationStateError, { OperationStateErrorCode } from '../errors/OperationStateError'; /** @@ -55,24 +55,24 @@ import OperationStateError, { OperationStateErrorCode } from '../errors/Operatio * depend on. Declared structurally so unit tests can hand in a mock * without pulling the real native binding into the test process. * - * The real binding's `Statement` (see `native/sea/index.d.ts`) has + * The real binding's `Statement` (see `native/kernel/index.d.ts`) has * additional methods (`fetchNextBatch`, `schema`) which the lifecycle * helpers deliberately don't touch — those belong to the results * feature's surface. */ -export interface SeaStatementHandle { +export interface KernelStatementHandle { cancel(): Promise; close(): Promise; } /** * Internal lifecycle state shared between the operation backend and - * these helpers. `SeaOperationBackend` keeps an instance of this and + * these helpers. `KernelOperationBackend` keeps an instance of this and * passes it to each helper call. Centralising the flags here means * the helpers stay pure (no `this`) and the backend stays * straightforward. */ -export interface SeaOperationLifecycleState { +export interface KernelOperationLifecycleState { /** True once `cancel()` has succeeded — subsequent fetch* must throw. */ isCancelled: boolean; /** True once `close()` has been called (idempotent). */ @@ -83,7 +83,7 @@ export interface SeaOperationLifecycleState { * Factory for a fresh lifecycle-state record. Helps keep test setup * tidy. */ -export function createLifecycleState(): SeaOperationLifecycleState { +export function createLifecycleState(): KernelOperationLifecycleState { return { isCancelled: false, isClosed: false }; } @@ -104,7 +104,7 @@ function rethrowKernelError(err: unknown): never { } /** - * Cancel an in-flight SEA operation. + * Cancel an in-flight kernel operation. * * Mirrors `DBSQLOperation.cancel` semantics * (`lib/DBSQLOperation.ts:241-259`): @@ -119,9 +119,9 @@ function rethrowKernelError(err: unknown): never { * - returns a `Status.success()` on success (no rich Thrift status * payload is available from the kernel side). */ -export async function seaCancel( - state: SeaOperationLifecycleState, - statement: SeaStatementHandle, +export async function kernelCancel( + state: KernelOperationLifecycleState, + statement: KernelStatementHandle, context: IClientContext, operationId: string, ): Promise { @@ -129,7 +129,7 @@ export async function seaCancel( return Status.success(); } - context.getLogger().log(LogLevel.debug, `Cancelling SEA operation with id: ${operationId}`); + context.getLogger().log(LogLevel.debug, `Cancelling kernel operation with id: ${operationId}`); // Set the intent BEFORE the RPC and keep it set even if the cancel RPC // fails: the caller asked to cancel, so the operation must stay cancelled @@ -149,7 +149,7 @@ export async function seaCancel( } /** - * Close a SEA operation. + * Close a kernel operation. * * Mirrors `DBSQLOperation.close` semantics * (`lib/DBSQLOperation.ts:265-284`) without the Thrift-only @@ -160,9 +160,9 @@ export async function seaCancel( * - sets the closed flag _before_ awaiting so a concurrent fetch * sees the closed state as soon as the await yields. */ -export async function seaClose( - state: SeaOperationLifecycleState, - statement: SeaStatementHandle, +export async function kernelClose( + state: KernelOperationLifecycleState, + statement: KernelStatementHandle, context: IClientContext, operationId: string, ): Promise { @@ -170,7 +170,7 @@ export async function seaClose( return Status.success(); } - context.getLogger().log(LogLevel.debug, `Closing SEA operation with id: ${operationId}`); + context.getLogger().log(LogLevel.debug, `Closing kernel operation with id: ${operationId}`); state.isClosed = true; @@ -204,7 +204,7 @@ function synthesizeFinishedStatus(): OperationStatus { * `IOperation.finished({progress, callback})` M0 implementation. * * The Thrift implementation is a 100ms polling loop over - * `getOperationStatus` (`lib/DBSQLOperation.ts:337-391`). For SEA M0, + * `getOperationStatus` (`lib/DBSQLOperation.ts:337-391`). For kernel M0, * the kernel's `Statement::execute().await` already blocks until the * statement reaches a terminal state — by the time the JS layer has * a `Statement` handle, the operation has already finished. @@ -221,8 +221,8 @@ function synthesizeFinishedStatus(): OperationStatus { * without throwing; throwing is the responsibility of subsequent * fetch calls). */ -export async function seaFinished( - state: SeaOperationLifecycleState, +export async function kernelFinished( + state: KernelOperationLifecycleState, options?: { progress?: boolean; callback?: (status: OperationStatus) => unknown; @@ -241,15 +241,15 @@ export async function seaFinished( } /** - * Pre-flight check used by fetch* methods on `SeaOperationBackend`. + * Pre-flight check used by fetch* methods on `KernelOperationBackend`. * If the operation has been cancelled or closed, throw the same * `OperationStateError` classes the facade uses. Keeping these typed lets - * callers branch on `OperationStateErrorCode` consistently for Thrift and SEA. + * callers branch on `OperationStateErrorCode` consistently for Thrift and kernel. * * Exported so impl-results can call it at the top of every fetch * call without duplicating the if/throw logic. */ -export function failIfNotActive(state: SeaOperationLifecycleState): void { +export function failIfNotActive(state: KernelOperationLifecycleState): void { if (state.isCancelled) { // Use the canonical `OperationStateError(Canceled)` (message "The operation // was canceled by a client") rather than a custom string, so the message diff --git a/lib/sea/SeaPositionalParams.ts b/lib/kernel/KernelPositionalParams.ts similarity index 92% rename from lib/sea/SeaPositionalParams.ts rename to lib/kernel/KernelPositionalParams.ts index 0089d018..758ec733 100644 --- a/lib/sea/SeaPositionalParams.ts +++ b/lib/kernel/KernelPositionalParams.ts @@ -14,11 +14,11 @@ import { DBSQLParameter, DBSQLParameterValue } from '../DBSQLParameter'; import ParameterError from '../errors/ParameterError'; -import { SeaNativeTypedValueInput, SeaNativeNamedTypedValueInput } from './SeaNativeLoader'; -import assertBindableValue from './SeaInputValidation'; +import { KernelNativeTypedValueInput, KernelNativeNamedTypedValueInput } from './KernelNativeLoader'; +import assertBindableValue from './KernelInputValidation'; /** - * Derive `(precision,scale)` from a decimal value string for the SEA + * Derive `(precision,scale)` from a decimal value string for the kernel * `DECIMAL(p,s)` type name — the kernel param codec requires the parenthesised * form (plain `"DECIMAL"` is rejected) so it preserves the caller's fractional * digits. `"99.99"` ⇒ `"4,2"`; `"-123"` ⇒ `"3,0"`; `"0.00001"` ⇒ `"5,5"`. @@ -66,7 +66,7 @@ function decimalPrecisionScale(v: string): string { * - INTERVAL * → `INTERVAL` (the codec's single interval type name) * - a missing value ⇒ SQL NULL (`parse_typed_value` maps `value: None` to NULL). */ -function toTypedValueInput(value: DBSQLParameter | DBSQLParameterValue): SeaNativeTypedValueInput { +function toTypedValueInput(value: DBSQLParameter | DBSQLParameterValue): KernelNativeTypedValueInput { const param = value instanceof DBSQLParameter ? value : new DBSQLParameter({ value }); const spark = param.toSparkParameter(); const stringValue = spark.value?.stringValue ?? undefined; @@ -93,9 +93,9 @@ function toTypedValueInput(value: DBSQLParameter | DBSQLParameterValue): SeaNati * when none were supplied, so the caller can keep the minimal no-options * call shape. */ -export function buildSeaPositionalParams( +export function buildKernelPositionalParams( ordinalParameters?: Array, -): Array | undefined { +): Array | undefined { if (ordinalParameters === undefined || ordinalParameters.length === 0) { return undefined; } @@ -111,9 +111,9 @@ export function buildSeaPositionalParams( * same `toTypedValueInput` mapping (DECIMAL → DECIMAL(p,s), NULL → VOID, …), * then carries its name. Returns `undefined` when none were supplied. */ -export function buildSeaNamedParams( +export function buildKernelNamedParams( namedParameters?: Record, -): Array | undefined { +): Array | undefined { if (namedParameters === undefined || Object.keys(namedParameters).length === 0) { return undefined; } diff --git a/lib/sea/SeaResultsProvider.ts b/lib/kernel/KernelResultsProvider.ts similarity index 90% rename from lib/sea/SeaResultsProvider.ts rename to lib/kernel/KernelResultsProvider.ts index ce34641c..b84fdca4 100644 --- a/lib/sea/SeaResultsProvider.ts +++ b/lib/kernel/KernelResultsProvider.ts @@ -14,7 +14,7 @@ import IResultsProvider, { ResultsProviderFetchNextOptions } from '../result/IResultsProvider'; import { ArrowBatch } from '../result/utils'; -import { countRowsInIpc, patchIpcBytes } from './SeaArrowIpc'; +import { countRowsInIpc, patchIpcBytes } from './KernelArrowIpc'; /** * The minimal slice of the napi-binding `Statement` class that we @@ -22,7 +22,7 @@ import { countRowsInIpc, patchIpcBytes } from './SeaArrowIpc'; * d.ts) so the loader layer's loose `unknown` typing doesn't force * unsafe casts at every call site, and so unit tests can pass a stub. */ -export interface SeaFetchHandle { +export interface KernelFetchHandle { fetchNextBatch(): Promise<{ ipcBytes: Buffer } | null>; } @@ -34,7 +34,7 @@ export interface SeaFetchHandle { * * Each kernel `fetchNextBatch()` call returns a complete Arrow IPC * stream (schema header + 1 record-batch message) per the design - * documented at `sea-workflow/findings/arch/napi-binding/round2-methods-2026-05-15.md:46-60`. + * documented at `kernel-workflow/findings/arch/napi-binding/round2-methods-2026-05-15.md:46-60`. * We pass that buffer through as a single-element `batches: [ipcBytes]` * array — `RecordBatchReader.from(arrowBatch.batches)` inside the * converter (`lib/result/ArrowResultConverter.ts:119`) reads the @@ -45,10 +45,10 @@ export interface SeaFetchHandle { * sum of `RecordBatch.numRows` across messages in the stream) because * the converter consumes that as an explicit field rather than * deriving it from the batch contents. See the comment in - * `SeaArrowIpc.ts:decodeIpcBatch` for the cost rationale. + * `KernelArrowIpc.ts:decodeIpcBatch` for the cost rationale. */ -export default class SeaResultsProvider implements IResultsProvider { - private readonly statement: SeaFetchHandle; +export default class KernelResultsProvider implements IResultsProvider { + private readonly statement: KernelFetchHandle; // Prefetched next batch so `hasMore()` can be answered without an // extra round-trip. Set by `prime()` (lazy) and by `fetchNext`. @@ -57,7 +57,7 @@ export default class SeaResultsProvider implements IResultsProvider // Set once the kernel returns `null` from `fetchNextBatch()`. private exhausted = false; - constructor(statement: SeaFetchHandle) { + constructor(statement: KernelFetchHandle) { this.statement = statement; } @@ -104,7 +104,7 @@ export default class SeaResultsProvider implements IResultsProvider // apache-arrow@13 (which predates Duration support) can decode the // stream. The downstream `RecordBatchReader.from` inside // `ArrowResultConverter` sees the same patched buffer. See - // `SeaArrowIpcDurationFix.ts`. + // `KernelArrowIpcDurationFix.ts`. const ipcBytes = patchIpcBytes(next.ipcBytes); // Row count only — `countRowsInIpc` reads the RecordBatch metadata // headers without materializing vectors (the converter re-decodes diff --git a/lib/sea/SeaServerInfo.ts b/lib/kernel/KernelServerInfo.ts similarity index 80% rename from lib/sea/SeaServerInfo.ts rename to lib/kernel/KernelServerInfo.ts index 955e006a..6e9f05af 100644 --- a/lib/sea/SeaServerInfo.ts +++ b/lib/kernel/KernelServerInfo.ts @@ -22,10 +22,10 @@ import { TGetInfoType, TGetInfoValue } from '../../thrift/TCLIService_types'; * the values client-side. * * The Databricks Thrift server itself answers only three `TGetInfoType`s and - * rejects every other value; we mirror that surface so the SEA path is a + * rejects every other value; we mirror that surface so the kernel path is a * drop-in equivalent: * - * | TGetInfoType | Thrift server | SEA (here) | + * | TGetInfoType | Thrift server | kernel (here) | * |---------------------|---------------|-------------------| * | CLI_SERVER_NAME (13)| "Spark SQL" | "Spark SQL" | * | CLI_DBMS_NAME (17)| "Spark SQL" | "Spark SQL" | @@ -36,15 +36,15 @@ import { TGetInfoType, TGetInfoValue } from '../../thrift/TCLIService_types'; * three values above are byte-identical to the server's, and the server * returns an error for every other `TGetInfoType` (probed CLI_MAX_DRIVER_- * CONNECTIONS, CLI_DATA_SOURCE_NAME, CLI_FETCH_DIRECTION, … — all errored). So - * the SEA "undefined → throw" path matches Thrift's effective behaviour rather + * the kernel "undefined → throw" path matches Thrift's effective behaviour rather * than narrowing it. */ /** Canonical DBMS product name — identical to the Thrift server's value. */ -export const SEA_DBMS_NAME = 'Spark SQL'; +export const KERNEL_DBMS_NAME = 'Spark SQL'; /** Server-name answer — identical to the Thrift server's value. */ -export const SEA_SERVER_NAME = 'Spark SQL'; +export const KERNEL_SERVER_NAME = 'Spark SQL'; /** * DBMS version string. Mirrors the constant the Databricks Thrift server @@ -52,22 +52,22 @@ export const SEA_SERVER_NAME = 'Spark SQL'; * the DBR release). Kept in lock-step with Thrift for parity; if the server * ever changes it the comparator's GET_INFO suite flags the drift. */ -export const SEA_DBMS_VERSION = '3.1.1'; +export const KERNEL_DBMS_VERSION = '3.1.1'; /** - * Synthesize the `TGetInfoValue` for a `getInfo` request on the SEA path. + * Synthesize the `TGetInfoValue` for a `getInfo` request on the kernel path. * Returns `undefined` for any `TGetInfoType` the (Thrift) server does not * answer — the caller surfaces that as an error, matching Thrift's * reject-unsupported-info-type behaviour. */ -export function seaServerInfoValue(infoType: number): TGetInfoValue | undefined { +export function kernelServerInfoValue(infoType: number): TGetInfoValue | undefined { switch (infoType) { case TGetInfoType.CLI_SERVER_NAME: - return new TGetInfoValue({ stringValue: SEA_SERVER_NAME }); + return new TGetInfoValue({ stringValue: KERNEL_SERVER_NAME }); case TGetInfoType.CLI_DBMS_NAME: - return new TGetInfoValue({ stringValue: SEA_DBMS_NAME }); + return new TGetInfoValue({ stringValue: KERNEL_DBMS_NAME }); case TGetInfoType.CLI_DBMS_VER: - return new TGetInfoValue({ stringValue: SEA_DBMS_VERSION }); + return new TGetInfoValue({ stringValue: KERNEL_DBMS_VERSION }); default: return undefined; } diff --git a/lib/sea/SeaSessionBackend.ts b/lib/kernel/KernelSessionBackend.ts similarity index 80% rename from lib/sea/SeaSessionBackend.ts rename to lib/kernel/KernelSessionBackend.ts index 34188ac6..6784042e 100644 --- a/lib/sea/SeaSessionBackend.ts +++ b/lib/kernel/KernelSessionBackend.ts @@ -33,30 +33,35 @@ import InfoValue from '../dto/InfoValue'; import HiveDriverError from '../errors/HiveDriverError'; import ParameterError from '../errors/ParameterError'; import { LogLevel } from '../contracts/IDBSQLLogger'; -import { SeaConnection, SeaNativeExecuteOptions, SeaStatement, SeaNativeAsyncStatement } from './SeaNativeLoader'; -import { decodeNapiKernelError } from './SeaErrorMapping'; -import SeaOperationBackend from './SeaOperationBackend'; -import { buildSeaPositionalParams, buildSeaNamedParams } from './SeaPositionalParams'; -import { seaServerInfoValue } from './SeaServerInfo'; +import { + KernelConnection, + KernelNativeExecuteOptions, + KernelStatement, + KernelNativeAsyncStatement, +} from './KernelNativeLoader'; +import { decodeNapiKernelError } from './KernelErrorMapping'; +import KernelOperationBackend from './KernelOperationBackend'; +import { buildKernelPositionalParams, buildKernelNamedParams } from './KernelPositionalParams'; +import { kernelServerInfoValue } from './KernelServerInfo'; import { serializeQueryTags } from '../utils'; -export interface SeaSessionBackendOptions { +export interface KernelSessionBackendOptions { /** The opaque napi `Connection` handle returned by `openSession`. */ - connection: SeaConnection; + connection: KernelConnection; context: IClientContext; /** Optional override for `id`. Defaults to a fresh UUIDv4. */ id?: string; } /** - * SEA-backed implementation of `ISessionBackend`. + * kernel-backed implementation of `ISessionBackend`. * * **Scope:** `executeStatement` (sync + async), `close`, `getInfo`, and the * full metadata surface (`getCatalogs`, `getSchemas`, `getTables`, * `getColumns`, `getFunctions`, `getTableTypes`, `getTypeInfo`, * `getPrimaryKeys`, `getCrossReference`) — each forwards to the kernel's napi * metadata calls (see `runMetadata`). The Thrift backend remains the default; - * callers opt into the kernel path via `ConnectionOptions.useSEA`. + * callers opt into the kernel path via `ConnectionOptions.useKernel`. * * **Session config flow:** catalog / schema / sessionConf are applied * once at session creation (kernel `Session::builder().defaults()` + @@ -75,12 +80,12 @@ export interface SeaSessionBackendOptions { * the opaque napi classes can't carry on the wire — the `awaitResult` probe is * the load-bearing feature-detect (see databricks-sql-kernel#140). */ -function isSeaAsyncStatement(x: SeaStatement | SeaNativeAsyncStatement): x is SeaNativeAsyncStatement { - return typeof (x as SeaNativeAsyncStatement).awaitResult === 'function'; +function isKernelAsyncStatement(x: KernelStatement | KernelNativeAsyncStatement): x is KernelNativeAsyncStatement { + return typeof (x as KernelNativeAsyncStatement).awaitResult === 'function'; } -export default class SeaSessionBackend implements ISessionBackend { - private readonly connection: SeaConnection; +export default class KernelSessionBackend implements ISessionBackend { + private readonly connection: KernelConnection; private readonly context: IClientContext; @@ -88,7 +93,7 @@ export default class SeaSessionBackend implements ISessionBackend { private closed = false; - constructor({ connection, context, id }: SeaSessionBackendOptions) { + constructor({ connection, context, id }: KernelSessionBackendOptions) { this.connection = connection; this.context = context; this._id = id ?? uuidv4(); @@ -99,29 +104,29 @@ export default class SeaSessionBackend implements ISessionBackend { } /** - * `getInfo` (JDBC `DatabaseMetaData` / ODBC `SQLGetInfo`) has no SEA/kernel + * `getInfo` (JDBC `DatabaseMetaData` / ODBC `SQLGetInfo`) has no kernel * endpoint, so — exactly as JDBC does for `DatabaseMetaData` — we synthesize * the answer client-side for the three `TGetInfoType`s the Databricks server * answers (server name / DBMS name / DBMS version) and reject the rest. * - * This is NOT a SEA-only contract narrowing: probing the live warehouse over + * This is NOT a kernel-only contract narrowing: probing the live warehouse over * the Thrift path confirms the server itself returns an error for every * other `TGetInfoType` (CLI_MAX_DRIVER_CONNECTIONS, CLI_DATA_SOURCE_NAME, …), * and the three values it does answer are byte-identical to the constants we * synthesize (`"Spark SQL"` / `"Spark SQL"` / `"3.1.1"`, re-verified live). * So rejecting an unsupported type matches Thrift's effective behaviour — we * just surface a clearer, typed error than the server's opaque one. See - * {@link seaServerInfoValue}. + * {@link kernelServerInfoValue}. */ public async getInfo(infoType: number): Promise { this.failIfClosed(); - const value = seaServerInfoValue(infoType); + const value = kernelServerInfoValue(infoType); if (value === undefined) { throw new HiveDriverError( - `SEA getInfo: unsupported TGetInfoType ${infoType}. Only the info types the Databricks ` + + `kernel getInfo: unsupported TGetInfoType ${infoType}. Only the info types the Databricks ` + `server itself answers are supported: CLI_SERVER_NAME (13), CLI_DBMS_NAME (17), ` + `CLI_DBMS_VER (18). The server rejects every other type on the Thrift path too, so this ` + - `is not a SEA-specific restriction.`, + `is not a kernel-specific restriction.`, ); } return new InfoValue(value); @@ -134,15 +139,15 @@ export default class SeaSessionBackend implements ISessionBackend { * Per-statement options forwarded to the kernel `ExecuteOptions`: * - `ordinalParameters` / `namedParameters` → bound params (mutually * exclusive — the kernel binds one placeholder style per statement); - * - `queryTimeout` → NO-OP on SEA (SQL Warehouses use `STATEMENT_TIMEOUT`); + * - `queryTimeout` → NO-OP on kernel (SQL Warehouses use `STATEMENT_TIMEOUT`); * never forwarded to the kernel and never applied as a client-side * deadline — see the note in `executeStatement`; - * - `rowLimit` → `rowLimit` (SEA-only server-side row cap); + * - `rowLimit` → `rowLimit` (kernel-only server-side row cap); * - `queryTags` → serialised into the conf overlay's reserved * `query_tags` key (the same wire shape Thrift's `serializeQueryTags` * produces), merged with any explicit `statementConf`. * - * Still rejected (genuinely unsupported on SEA, rather than silently + * 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 @@ -153,24 +158,24 @@ export default class SeaSessionBackend implements ISessionBackend { if (options.useCloudFetch !== undefined) { throw new HiveDriverError( - 'SEA executeStatement: useCloudFetch is controlled by the kernel result configuration and is not a per-statement option on SEA', + 'kernel executeStatement: useCloudFetch is controlled by the kernel result configuration and is not a per-statement option on kernel', ); } if (options.useLZ4Compression !== undefined) { throw new HiveDriverError( - 'SEA executeStatement: useLZ4Compression is not supported on SEA (result compression is governed by the kernel)', + 'kernel executeStatement: useLZ4Compression is not supported on kernel (result compression is governed by the kernel)', ); } if (options.stagingAllowedLocalPath !== undefined) { throw new HiveDriverError( - 'SEA executeStatement: stagingAllowedLocalPath (volume operations) is not supported on SEA', + 'kernel executeStatement: stagingAllowedLocalPath (volume operations) is not supported on kernel', ); } - // `runAsync` selects the kernel execution path. NOTE: this is a SEA/kernel- + // `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 - // there. The only observable difference between the two SEA paths is WHEN + // there. The only observable difference between the two kernel paths is WHEN // `executeStatement` resolves; the public API, result shape, schema, and // error classes are identical on both (and to Thrift). See the option's // JSDoc in `IDBSQLSession` for the cross-backend contract. @@ -191,11 +196,11 @@ export default class SeaSessionBackend implements ISessionBackend { // materialises results via `awaitResult()`. `queryTimeout` is a no-op // here too. const runAsync = options.runAsync ?? false; - // `queryTimeout` is a NO-OP on the SEA (kernel) backend. It is the JDBC + // `queryTimeout` is a NO-OP on the kernel (kernel) backend. It is the JDBC // `setQueryTimeout` knob which — per the option's JSDoc — is effective only - // on Compute clusters; SQL Warehouses (what SEA targets) use the + // on Compute clusters; SQL Warehouses (what kernel targets) use the // `STATEMENT_TIMEOUT` SQL/session conf instead. We deliberately do NOT map it - // to the SEA `wait_timeout` field: `wait_timeout` is the server's inline-hold + // to the kernel `wait_timeout` field: `wait_timeout` is the server's inline-hold // window (the time the POST blocks for results, capped 5–50s), NOT a // statement-execution timeout — mapping it there silently caps the timeout at // 50s, rejects <5s with HTTP 400, and conflates the inline wait with @@ -213,7 +218,7 @@ export default class SeaSessionBackend implements ISessionBackend { // release (no client-side drive-to-terminal). Fire-and-forget DDL/DML // commits because the server runs it inline during the POST. const execOptions = this.buildExecuteOptions(options); - let direct: SeaStatement | SeaNativeAsyncStatement; + let direct: KernelStatement | KernelNativeAsyncStatement; try { direct = execOptions === undefined @@ -229,19 +234,19 @@ export default class SeaSessionBackend implements ISessionBackend { if (direct === null || direct === undefined) { throw this.logAndMapError( 'executeStatement', - new HiveDriverError('SEA executeStatementDirect returned no statement handle'), + new HiveDriverError('kernel executeStatementDirect returned no statement handle'), ); } // Feature-detect the arm via a type guard: only the Running `AsyncStatement` // exposes `awaitResult`; the terminal `Statement` (Completed arm) does not. - if (isSeaAsyncStatement(direct)) { - return new SeaOperationBackend({ asyncStatement: direct, context: this.context }); + if (isKernelAsyncStatement(direct)) { + return new KernelOperationBackend({ asyncStatement: direct, context: this.context }); } - return new SeaOperationBackend({ statement: direct, context: this.context }); + return new KernelOperationBackend({ statement: direct, context: this.context }); } // Async path: submit (`wait_timeout=0s`) returns a pending handle the - // backend polls. (`queryTimeout` is a no-op on SEA — see the note above.) + // backend polls. (`queryTimeout` is a no-op on kernel — see the note above.) const execOptions = this.buildExecuteOptions(options); let asyncStatement; try { @@ -252,9 +257,9 @@ export default class SeaSessionBackend implements ISessionBackend { } catch (err) { throw this.logAndMapError('executeStatement', err); } - // `queryTimeout` is a no-op on SEA (see the note at the top of this method); + // `queryTimeout` is a no-op on kernel (see the note at the top of this method); // not forwarded to the kernel and not applied as a client-side deadline. - return new SeaOperationBackend({ + return new KernelOperationBackend({ asyncStatement: asyncStatement!, context: this.context, }); @@ -265,25 +270,25 @@ export default class SeaSessionBackend implements ISessionBackend { * `ExecuteOptions`, returning `undefined` when nothing is set so the * no-options call shape (`executeStatement(sql)`) is preserved. */ - private buildExecuteOptions(options: ExecuteStatementOptions): SeaNativeExecuteOptions | undefined { + private buildExecuteOptions(options: ExecuteStatementOptions): KernelNativeExecuteOptions | undefined { // Positional (`?`) and named (`:name`) parameters are mutually exclusive — // the kernel binds one placeholder style per statement. Use the SAME error // type and message as the Thrift backend (`ThriftSessionBackend`) so a // caller catching `ParameterError` behaves identically across backends. - const positionalParams = buildSeaPositionalParams(options.ordinalParameters); - const namedParams = buildSeaNamedParams(options.namedParameters); + const positionalParams = buildKernelPositionalParams(options.ordinalParameters); + const namedParams = buildKernelNamedParams(options.namedParameters); if (positionalParams !== undefined && namedParams !== undefined) { throw new ParameterError('Driver does not support both ordinal and named parameters.'); } - const execOptions: SeaNativeExecuteOptions = {}; + const execOptions: KernelNativeExecuteOptions = {}; if (positionalParams !== undefined) { execOptions.positionalParams = positionalParams; } if (namedParams !== undefined) { execOptions.namedParams = namedParams; } - // NB: `queryTimeout` is intentionally NOT forwarded — it is a no-op on SEA + // NB: `queryTimeout` is intentionally NOT forwarded — it is a no-op on kernel // (SQL Warehouses use `STATEMENT_TIMEOUT`; mapping it to `wait_timeout` would // abuse the inline-hold window). See the note in `executeStatement`. if (options.rowLimit !== undefined) { @@ -310,8 +315,8 @@ export default class SeaSessionBackend implements ISessionBackend { } /** Wrap a napi metadata `Statement` (already terminal) as an operation backend. */ - private wrapStatement(nativeStatement: SeaStatement): IOperationBackend { - return new SeaOperationBackend({ + private wrapStatement(nativeStatement: KernelStatement): IOperationBackend { + return new KernelOperationBackend({ statement: nativeStatement, context: this.context, id: nativeStatement.statementId, @@ -375,13 +380,13 @@ export default class SeaSessionBackend implements ISessionBackend { 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 SEA/kernel path cannot, + // 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( - 'SEA getPrimaryKeys requires a catalog — pass `catalogName` explicitly. (The Thrift backend ' + - 'can omit it and let the server resolve a default; the SEA kernel path requires it.)', + '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(() => @@ -404,8 +409,8 @@ export default class SeaSessionBackend implements ISessionBackend { } /** Run a napi metadata call, mapping kernel errors and wrapping the result handle. */ - private async runMetadata(call: () => Promise): Promise { - let nativeStatement: SeaStatement; + private async runMetadata(call: () => Promise): Promise { + let nativeStatement: KernelStatement; try { nativeStatement = await call(); } catch (err) { @@ -416,13 +421,13 @@ export default class SeaSessionBackend implements ISessionBackend { /** * Map a napi/kernel error to a typed driver error and emit a debug breadcrumb - * first, matching the rest of the SEA backend's logging convention - * (`SeaOperationLifecycle` / `SeaOperationBackend`). Metadata and bound-param + * first, matching the rest of the kernel backend's logging convention + * (`KernelOperationLifecycle` / `KernelOperationBackend`). Metadata and bound-param * execute failures otherwise threw with no on-call signal. */ private logAndMapError(op: string, err: unknown): Error { const mapped = decodeNapiKernelError(err); - this.context.getLogger().log(LogLevel.debug, `SEA ${op} failed for session ${this._id}: ${mapped.message}`); + this.context.getLogger().log(LogLevel.debug, `kernel ${op} failed for session ${this._id}: ${mapped.message}`); return mapped; } @@ -441,7 +446,7 @@ export default class SeaSessionBackend implements ISessionBackend { private failIfClosed(): void { if (this.closed) { - throw new HiveDriverError('SeaSessionBackend: session is closed'); + throw new HiveDriverError('KernelSessionBackend: session is closed'); } } } diff --git a/lib/result/ArrowResultConverter.ts b/lib/result/ArrowResultConverter.ts index 3902ac25..3bb9014a 100644 --- a/lib/result/ArrowResultConverter.ts +++ b/lib/result/ArrowResultConverter.ts @@ -28,17 +28,17 @@ type ArrowSchemaField = Field>; /** * Metadata key carrying the original Arrow `Duration` time unit on fields - * rewritten to `Int64` by the SEA IPC pre-processor - * (`lib/sea/SeaArrowIpcDurationFix.ts`). Re-declared here (rather than + * rewritten to `Int64` by the kernel IPC pre-processor + * (`lib/kernel/KernelArrowIpcDurationFix.ts`). Re-declared here (rather than * imported) to keep this generic `lib/result` converter free of a - * compile-time dependency on `lib/sea`. + * compile-time dependency on `lib/kernel`. * - * **SEA-gated by construction — NOT shared with Thrift.** This key (and the + * **kernel-gated by construction — NOT shared with Thrift.** This key (and the * `DataType.isInterval` / duration branches below) only ever execute on the - * SEA path. The Thrift backend sets `intervalTypesAsArrow: false` and maps + * kernel path. The Thrift backend sets `intervalTypesAsArrow: false` and maps * both INTERVAL `TTypeId`s to `ArrowString` (`lib/result/utils.ts`), so the * server pre-formats intervals to strings and this logic is never reached. - * `export`ed so `SeaIntervalParity.test` can pin it equal to the SEA-side + * `export`ed so `KernelIntervalParity.test` can pin it equal to the kernel-side * declaration and catch a rename/typo that would silently no-op the consumer. */ export const DURATION_UNIT_METADATA_KEY = 'databricks.arrow.duration_unit'; @@ -67,7 +67,7 @@ const NS_PER_DAY = NS_PER_HOUR * BigInt(24); function formatArrowInterval(value: Int32Array, valueType: Interval): string { if (valueType?.unit !== IntervalUnit.YEAR_MONTH) { throw new HiveDriverError( - `SEA result converter: unsupported Arrow Interval unit ${valueType?.unit}. The kernel emits only ` + + `kernel result converter: unsupported Arrow Interval unit ${valueType?.unit}. The kernel emits only ` + `YEAR_MONTH as a native Arrow Interval (DAY-TIME arrives as Duration); MONTH_DAY_NANO is unsupported.`, ); } @@ -94,14 +94,14 @@ function formatYearMonth(years: number, months: number): string { } /** - * Format an Arrow `Duration` value (rewritten by the SEA IPC + * Format an Arrow `Duration` value (rewritten by the kernel IPC * pre-processor to `Int64`) into the thrift INTERVAL DAY-TIME string. * * @param value the duration value as `bigint` (signed nanos/micros/ * millis/seconds depending on `unit`) * @param unit one of `SECOND` / `MILLISECOND` / `MICROSECOND` / * `NANOSECOND` (the original Arrow time unit, captured - * by `SeaArrowIpcDurationFix.ts`) + * by `KernelArrowIpcDurationFix.ts`) */ function formatDurationToIntervalDayTime(value: bigint | number, unit: string): string { const bi = typeof value === 'bigint' ? value : BigInt(value); @@ -119,7 +119,7 @@ function formatDurationToIntervalDayTime(value: bigint | number, unit: string): * * Throws on any other unit rather than silently treating it as * NANOSECOND. The four units above are exactly what - * `SeaArrowIpcDurationFix` enumerates when it stamps the + * `KernelArrowIpcDurationFix` enumerates when it stamps the * `databricks.arrow.duration_unit` metadata, so an unrecognized unit * here means the two sides have drifted — fail loud (matching * `formatArrowInterval`'s stance) instead of emitting a confidently @@ -137,7 +137,7 @@ function toNanoseconds(value: bigint, unit: string): bigint { return value; default: throw new HiveDriverError( - `SEA INTERVAL DAY-TIME: unrecognized Arrow duration unit ${JSON.stringify(unit)}; ` + + `kernel INTERVAL DAY-TIME: unrecognized Arrow duration unit ${JSON.stringify(unit)}; ` + `expected one of SECOND / MILLISECOND / MICROSECOND / NANOSECOND`, ); } @@ -190,7 +190,7 @@ export default class ArrowResultConverter implements IResultsProvider // Only the column `schema` is consumed here. Typed as the minimal shape // (not the full Thrift `TGetResultSetMetadataResp`) so both the Thrift - // operation backend and the SEA backend's neutral `ResultMetadata` — + // operation backend and the kernel backend's neutral `ResultMetadata` — // which both carry `schema?: TTableSchema` — can construct the converter // without an adapter at the call site. constructor(context: IClientContext, source: IResultsProvider, { schema }: { schema?: TTableSchema }) { @@ -357,12 +357,12 @@ export default class ArrowResultConverter implements IResultsProvider return new Date(value); } - // INTERVAL — Spark/Databricks SEA emits two flavours: native Arrow + // INTERVAL — Spark/Databricks kernel emits two flavours: native Arrow // `Interval[YearMonth]` / `Interval[DayTime]` (handled here) and // `Duration` (transparently rewritten to `Int64` upstream by - // `SeaArrowIpcDurationFix.ts`; handled in the bigint/Int64 branch + // `KernelArrowIpcDurationFix.ts`; handled in the bigint/Int64 branch // below). In every case we coerce to the canonical thrift string - // form so the SEA path is byte-identical with the thrift path: + // form so the kernel path is byte-identical with the thrift path: // YEAR-MONTH → `"Y-M"` // DAY-TIME → `"D HH:mm:ss.fffffffff"` if (DataType.isInterval(valueType)) { diff --git a/lib/telemetry/types.ts b/lib/telemetry/types.ts index 6434bf1e..292cc52e 100644 --- a/lib/telemetry/types.ts +++ b/lib/telemetry/types.ts @@ -120,7 +120,7 @@ export interface TelemetryEvent { * cloudfetch effectiveness by backend without a metrics-schema migration. * Optional for back-compat with already-emitted Thrift-only events. */ - backend?: 'thrift' | 'sea' | 'kernel'; + backend?: 'thrift' | 'kernel'; /** Timestamp when the event occurred (milliseconds since epoch) */ timestamp: number; @@ -258,7 +258,7 @@ export interface DriverConfiguration { * non-Thrift backend so per-connection slicing in metrics is possible. * Optional for back-compat with snapshots taken before this field landed. */ - backend?: 'thrift' | 'sea' | 'kernel'; + backend?: 'thrift' | 'kernel'; /** Node.js version */ nodeVersion: string; diff --git a/lib/thrift-backend/ThriftSessionBackend.ts b/lib/thrift-backend/ThriftSessionBackend.ts index d8db377e..de3f6e8a 100644 --- a/lib/thrift-backend/ThriftSessionBackend.ts +++ b/lib/thrift-backend/ThriftSessionBackend.ts @@ -170,7 +170,7 @@ export default class ThriftSessionBackend implements ISessionBackend { const driver = await this.context.getDriver(); const clientConfig = this.context.getConfig(); - // `rowLimit` / `statementConf` are kernel-backend (SEA) options with no + // `rowLimit` / `statementConf` are kernel-backend (kernel) options with no // Thrift wire equivalent. Surface a debug breadcrumb rather than dropping // them silently, so a caller that set them on the Thrift path has signal. if (options.rowLimit !== undefined || options.statementConf !== undefined) { @@ -178,9 +178,9 @@ export default class ThriftSessionBackend implements ISessionBackend { .getLogger() .log( LogLevel.warn, - 'ThriftSessionBackend.executeStatement: rowLimit / statementConf are kernel-backend (useSEA) ' + + 'ThriftSessionBackend.executeStatement: rowLimit / statementConf are kernel-backend (useKernel) ' + 'options with no Thrift wire equivalent — they are IGNORED on the Thrift path (e.g. rowLimit ' + - 'will not cap the result set). Use the kernel backend (useSEA) to honour them.', + 'will not cap the result set). Use the kernel backend (useKernel) to honour them.', ); } diff --git a/lib/thrift-backend/wireSynthesis.ts b/lib/thrift-backend/wireSynthesis.ts index 573e1171..64478f35 100644 --- a/lib/thrift-backend/wireSynthesis.ts +++ b/lib/thrift-backend/wireSynthesis.ts @@ -75,7 +75,7 @@ function resultFormatToThrift(format: ResultFormat): TSparkRowSetType { /** * Synthesize a Thrift `TGetOperationStatusResp` from the neutral * `OperationStatus` DTO. Used by `DBSQLOperation.status()` when running - * against a non-Thrift backend (e.g. SEA) so the public API stays Thrift-shaped. + * against a non-Thrift backend (e.g. kernel) so the public API stays Thrift-shaped. * * Lossy by design: Thrift-only fields not carried by `OperationStatus` * (`taskStatus`, `numModifiedRows`, `operationStarted`, `operationCompleted`, diff --git a/native/sea/README.md b/native/kernel/README.md similarity index 93% rename from native/sea/README.md rename to native/kernel/README.md index 6adfa944..a1b9c22c 100644 --- a/native/sea/README.md +++ b/native/kernel/README.md @@ -1,4 +1,4 @@ -# `native/sea/` — consumer-side directory for the Rust napi binding +# `native/kernel/` — consumer-side directory for the Rust napi binding **The Rust binding source lives in the kernel repo** at `databricks-sql-kernel/napi/`. Building it requires a local checkout @@ -27,7 +27,7 @@ and reintroduce the same clash. Standalone-workspace is the fix. ## What lives in this directory -- `index.d.ts` — TypeScript declarations consumed by `lib/sea/`. +- `index.d.ts` — TypeScript declarations consumed by `lib/kernel/`. Generated by napi-rs from the Rust source; checked in as the consumer-facing type contract. - `index.js` — napi-rs's per-platform router shim. Gitignored; @@ -57,7 +57,7 @@ nodejs repo. At release time the kernel's CI publishes `@databricks/databricks-sql-kernel-` npm packages — one per supported -platform — each containing a single `.node` binary. `native/sea/index.js` +platform — each containing a single `.node` binary. `native/kernel/index.js` (the napi-rs router) `require()`s the package matching the consumer's `process.platform` / `process.arch` at load time. @@ -77,7 +77,7 @@ M0 targets a **single** triple: **`linux-x64-gnu`** (package `@databricks/databricks-sql-kernel-linux-x64-gnu`, once published). On every other platform (macOS, Windows, linux-arm64, linux-x64-musl -/ Alpine, …) the SEA binding is simply absent: `SeaNativeLoader` +/ Alpine, …) the kernel binding is simply absent: `KernelNativeLoader` returns `undefined` from `tryGet()` / throws a structured `MODULE_NOT_FOUND` hint from `get()`, and the driver continues to use the Thrift backend exclusively. This is expected, not a regression — diff --git a/native/sea/index.d.ts b/native/kernel/index.d.ts similarity index 100% rename from native/sea/index.d.ts rename to native/kernel/index.d.ts diff --git a/native/sea/index.js b/native/kernel/index.js similarity index 100% rename from native/sea/index.js rename to native/kernel/index.js diff --git a/package.json b/package.json index 02f8eeca..94658e94 100644 --- a/package.json +++ b/package.json @@ -17,8 +17,8 @@ "test": "nyc --report-dir=${NYC_REPORT_DIR:-coverage_unit} mocha --config tests/unit/.mocharc.js", "update-version": "node bin/update-version.js && prettier --write ./lib/version.ts", "build": "npm run update-version && tsc --project tsconfig.build.json", - "build:native": "bash -c 'cd ${DATABRICKS_SQL_KERNEL_REPO:-../../databricks-sql-kernel}/napi && npx --yes @napi-rs/cli@2.18.4 build --platform ${BUILD_PROFILE:---release} && cp index.* $OLDPWD/native/sea/'", - "prepack": "test -f native/sea/index.js || { echo 'ERROR: native/sea/index.js (napi-rs router) is missing — the published tarball would fail to load SEA. It is committed to git; run `npm run build:native` if you removed it.' >&2; exit 1; }", + "build:native": "bash -c 'cd ${DATABRICKS_SQL_KERNEL_REPO:-../../databricks-sql-kernel}/napi && npx --yes @napi-rs/cli@2.18.4 build --platform ${BUILD_PROFILE:---release} && cp index.* $OLDPWD/native/kernel/'", + "prepack": "test -f native/kernel/index.js || { echo 'ERROR: native/kernel/index.js (napi-rs router) is missing — the published tarball would fail to load kernel. It is committed to git; run `npm run build:native` if you removed it.' >&2; exit 1; }", "watch": "tsc --project tsconfig.build.json --watch", "type-check": "tsc --noEmit", "prettier": "prettier . --check", diff --git a/tests/e2e/sea/auth-m2m-e2e.test.ts b/tests/e2e/kernel/auth-m2m-e2e.test.ts similarity index 92% rename from tests/e2e/sea/auth-m2m-e2e.test.ts rename to tests/e2e/kernel/auth-m2m-e2e.test.ts index debd74a7..7fda10e0 100644 --- a/tests/e2e/sea/auth-m2m-e2e.test.ts +++ b/tests/e2e/kernel/auth-m2m-e2e.test.ts @@ -15,14 +15,14 @@ import { expect } from 'chai'; import { DBSQLClient } from '../../../lib'; import AuthenticationError from '../../../lib/errors/AuthenticationError'; -import { isBlankOrReserved } from '../../../lib/sea/SeaAuth'; +import { isBlankOrReserved } from '../../../lib/kernel/KernelAuth'; import { ConnectionOptions } from '../../../lib/contracts/IDBSQLClient'; import { InternalConnectionOptions } from '../../../lib/contracts/InternalConnectionOptions'; /** - * sea-auth M1 OAuth M2M end-to-end: + * kernel-auth M1 OAuth M2M end-to-end: * 1. Construct a DBSQLClient. - * 2. `connect({ useSEA: true, authType: 'databricks-oauth', oauthClientId, + * 2. `connect({ useKernel: true, authType: 'databricks-oauth', oauthClientId, * oauthClientSecret })` against pecotesting. * 3. `openSession()` — kernel runs OIDC discovery + client_credentials * exchange. Successful openSession is the proof that the kernel-side @@ -31,7 +31,7 @@ import { InternalConnectionOptions } from '../../../lib/contracts/InternalConnec * 4. Close the session, then the client. * * No query is executed here — execution is the responsibility of the - * sea-execution feature's own e2e (mirror of the M0 PAT e2e scope at + * kernel-execution feature's own e2e (mirror of the M0 PAT e2e scope at * `auth-pat-e2e.test.ts`). If kernel-side OAuth fails, `openSession()` * raises before returning. * @@ -44,7 +44,7 @@ import { InternalConnectionOptions } from '../../../lib/contracts/InternalConnec * Skipped (not failed) when any of the four env vars is missing, so CI * machines without OAuth credentials don't fail-flap. */ -describe('sea-auth e2e — OAuth M2M through DBSQLClient ↔ SeaBackend ↔ napi binding', function suite() { +describe('kernel-auth e2e — OAuth M2M through DBSQLClient ↔ KernelBackend ↔ napi binding', function suite() { const host = process.env.DATABRICKS_PECOTESTING_SERVER_HOSTNAME; const path = process.env.DATABRICKS_PECOTESTING_HTTP_PATH2; const oauthClientId = process.env.DATABRICKS_PECOTESTING_AAD_CLIENT_ID; @@ -76,7 +76,7 @@ describe('sea-auth e2e — OAuth M2M through DBSQLClient ↔ SeaBackend ↔ napi authType: 'databricks-oauth', oauthClientId: oauthClientId as string, oauthClientSecret: oauthClientSecret as string, - useSEA: true, + useKernel: true, } as ConnectionOptions & InternalConnectionOptions); expect(connected).to.equal(client); @@ -103,7 +103,7 @@ describe('sea-auth e2e — OAuth M2M through DBSQLClient ↔ SeaBackend ↔ napi authType: 'databricks-oauth', oauthClientId: oauthClientId as string, oauthClientSecret: 'definitely-not-the-real-secret-deadbeef', - useSEA: true, + useKernel: true, } as ConnectionOptions & InternalConnectionOptions); let caught: unknown; diff --git a/tests/e2e/sea/auth-pat-e2e.test.ts b/tests/e2e/kernel/auth-pat-e2e.test.ts similarity index 88% rename from tests/e2e/sea/auth-pat-e2e.test.ts rename to tests/e2e/kernel/auth-pat-e2e.test.ts index d061d5b2..216d2388 100644 --- a/tests/e2e/sea/auth-pat-e2e.test.ts +++ b/tests/e2e/kernel/auth-pat-e2e.test.ts @@ -18,14 +18,14 @@ import { ConnectionOptions } from '../../../lib/contracts/IDBSQLClient'; import { InternalConnectionOptions } from '../../../lib/contracts/InternalConnectionOptions'; /** - * sea-auth M0 end-to-end: + * kernel-auth M0 end-to-end: * 1. Construct a DBSQLClient. - * 2. `connect({ useSEA: true, token })` against pecotesting. + * 2. `connect({ useKernel: true, token })` against pecotesting. * 3. `openSession()` — round-trips through the napi binding. * 4. Close the session, then the client. * * No query is executed here — execution is the responsibility of the - * sea-execution feature's own e2e. This test exists solely to confirm + * kernel-execution feature's own e2e. This test exists solely to confirm * the PAT round-trips end-to-end and the napi binding's `openSession` * surface is reachable from `DBSQLClient`. * @@ -38,7 +38,7 @@ import { InternalConnectionOptions } from '../../../lib/contracts/InternalConnec * If any of the three required env vars is missing, the suite is skipped * so CI machines without secrets don't fail-flap. */ -describe('sea-auth e2e — PAT through DBSQLClient ↔ SeaBackend ↔ napi binding', function suite() { +describe('kernel-auth e2e — PAT through DBSQLClient ↔ KernelBackend ↔ napi binding', function suite() { const host = process.env.DATABRICKS_PECOTESTING_SERVER_HOSTNAME; const path = process.env.DATABRICKS_PECOTESTING_HTTP_PATH; const token = process.env.DATABRICKS_PECOTESTING_TOKEN_PERSONAL || process.env.DATABRICKS_PECOTESTING_TOKEN; @@ -59,10 +59,10 @@ describe('sea-auth e2e — PAT through DBSQLClient ↔ SeaBackend ↔ napi bindi host: host as string, path: path as string, token: token as string, - // `useSEA` is an internal opt-in (InternalConnectionOptions), not a + // `useKernel` is an internal opt-in (InternalConnectionOptions), not a // public ConnectionOptions field — cast exactly as DBSQLClient.connect // does internally so the literal passes excess-property checking. - useSEA: true, + useKernel: true, } as ConnectionOptions & InternalConnectionOptions); expect(connected).to.equal(client); diff --git a/tests/e2e/sea/auth-u2m-e2e.test.ts b/tests/e2e/kernel/auth-u2m-e2e.test.ts similarity index 88% rename from tests/e2e/sea/auth-u2m-e2e.test.ts rename to tests/e2e/kernel/auth-u2m-e2e.test.ts index 923d5f0e..00287c10 100644 --- a/tests/e2e/sea/auth-u2m-e2e.test.ts +++ b/tests/e2e/kernel/auth-u2m-e2e.test.ts @@ -18,7 +18,7 @@ import { ConnectionOptions } from '../../../lib/contracts/IDBSQLClient'; import { InternalConnectionOptions } from '../../../lib/contracts/InternalConnectionOptions'; /** - * sea-auth M1 OAuth U2M end-to-end — **SKIPPED pending browser harness**. + * kernel-auth M1 OAuth U2M end-to-end — **SKIPPED pending browser harness**. * * U2M is interactive: the kernel opens a system browser * (`auth/oauth/u2m.rs:414`, via the `open` crate), binds a local @@ -32,11 +32,11 @@ import { InternalConnectionOptions } from '../../../lib/contracts/InternalConnec * suite carries a slot for whoever lands the harness work. * * The intended assertion sequence (mirrors `auth-m2m-e2e.test.ts`): - * 1. `client.connect({ useSEA: true, authType: 'databricks-oauth' })` + * 1. `client.connect({ useKernel: true, authType: 'databricks-oauth' })` * — NO `oauthClientSecret` → kernel picks the U2M flow. * 2. `openSession()` — kernel opens browser, waits for callback on * localhost:8030, exchanges the auth code, returns Bearer token, - * issues the create-session request to SEA. + * issues the create-session request to kernel. * 3. `session.close()` then `client.close()`. * * Required env (gated additionally via `it.skip` until the harness @@ -45,7 +45,7 @@ import { InternalConnectionOptions } from '../../../lib/contracts/InternalConnec * - DATABRICKS_PECOTESTING_HTTP_PATH * - (no client_id/secret — U2M uses kernel default `databricks-cli`) */ -describe('sea-auth e2e — OAuth U2M through DBSQLClient ↔ SeaBackend ↔ napi binding', function suite() { +describe('kernel-auth e2e — OAuth U2M through DBSQLClient ↔ KernelBackend ↔ napi binding', function suite() { this.timeout(300_000); it.skip('[pending TBD-oauth_u2m_test_harness] interactive U2M round-trip', async () => { @@ -58,7 +58,7 @@ describe('sea-auth e2e — OAuth U2M through DBSQLClient ↔ SeaBackend ↔ napi host, path, authType: 'databricks-oauth', - useSEA: true, + useKernel: true, } as ConnectionOptions & InternalConnectionOptions); expect(connected).to.equal(client); diff --git a/tests/e2e/sea/e2e-smoke.test.ts b/tests/e2e/kernel/e2e-smoke.test.ts similarity index 85% rename from tests/e2e/sea/e2e-smoke.test.ts rename to tests/e2e/kernel/e2e-smoke.test.ts index e96efe34..4f2bd9aa 100644 --- a/tests/e2e/sea/e2e-smoke.test.ts +++ b/tests/e2e/kernel/e2e-smoke.test.ts @@ -14,7 +14,7 @@ import { expect } from 'chai'; import { tableFromIPC } from 'apache-arrow'; -import { tryGetSeaNative, SeaConnection, SeaStatement } from '../../../lib/sea/SeaNativeLoader'; +import { tryGetKernelNative, KernelConnection, KernelStatement } from '../../../lib/kernel/KernelNativeLoader'; import config from '../utils/config'; // End-to-end smoke test against a live warehouse: @@ -28,24 +28,24 @@ import config from '../utils/config'; // used by every other e2e test, so `npm run e2e` has one consistent // skip/fail contract rather than two. -describe('SEA native binding — end-to-end smoke', function smoke() { +describe('kernel native binding — end-to-end smoke', function smoke() { // Live-warehouse tests can take >2s through warm-up. this.timeout(60_000); - const binding = tryGetSeaNative(); + const binding = tryGetKernelNative(); if (binding === undefined) { // Optional dependency absent on this platform — never reach the live path. - it.skip('SEA native binding not available on this platform'); + it.skip('kernel native binding not available on this platform'); return; } const { host: hostName, path: httpPath, token } = config; it('opens a session, runs SELECT 1, decodes the IPC payload to 1', async () => { - const connection: SeaConnection = await binding.openSession({ hostName, httpPath, token }); + const connection: KernelConnection = await binding.openSession({ hostName, httpPath, token }); expect(connection).to.be.an('object'); - let statement: SeaStatement | null = null; + let statement: KernelStatement | null = null; try { statement = await connection.executeStatement('SELECT 1'); expect(statement).to.be.an('object'); @@ -76,7 +76,7 @@ describe('SEA native binding — end-to-end smoke', function smoke() { }); it('returns a schema IPC payload before any batch is fetched', async () => { - const connection: SeaConnection = await binding.openSession({ hostName, httpPath, token }); + const connection: KernelConnection = await binding.openSession({ hostName, httpPath, token }); try { const statement = await connection.executeStatement('SELECT 1'); try { diff --git a/tests/e2e/sea/execution-e2e.test.ts b/tests/e2e/kernel/execution-e2e.test.ts similarity index 85% rename from tests/e2e/sea/execution-e2e.test.ts rename to tests/e2e/kernel/execution-e2e.test.ts index 28dd1035..d1ffc9ba 100644 --- a/tests/e2e/sea/execution-e2e.test.ts +++ b/tests/e2e/kernel/execution-e2e.test.ts @@ -18,16 +18,16 @@ import { ConnectionOptions } from '../../../lib/contracts/IDBSQLClient'; import { InternalConnectionOptions } from '../../../lib/contracts/InternalConnectionOptions'; /** - * sea-execution end-to-end test. + * kernel-execution end-to-end test. * - * Walks the full `DBSQLClient` → `SeaBackend` → napi binding → kernel + * Walks the full `DBSQLClient` → `KernelBackend` → napi binding → kernel * pipeline against a live warehouse over PAT: * - * 1. `connect({ useSEA: true })` selects the SEA backend. + * 1. `connect({ useKernel: true })` selects the kernel backend. * 2. `openSession({ initialCatalog: 'main' })` opens a kernel session * and threads `initialCatalog` through to the napi `ExecuteOptions`. * 3. `executeStatement('SELECT 1')` returns an `IOperation` backed by - * `SeaOperationBackend` (wraps a napi `Statement`). + * `KernelOperationBackend` (wraps a napi `Statement`). * 4. `operation.id` is observable (via `IOperation.id` on the public * surface). * 5. `operation.cancel()` and `operation.close()` succeed without @@ -39,13 +39,13 @@ import { InternalConnectionOptions } from '../../../lib/contracts/InternalConnec * provisioned secrets don't flap. * * **Proxy-validation note (per execution plan §17.4):** M0 verifies - * "no thrift fallback" indirectly — by selecting `useSEA: true` and + * "no thrift fallback" indirectly — by selecting `useKernel: true` and * exercising the executeStatement path. A proxy that captures * `executeStatement` + `GetStatement` wire counts lands in the - * sea-integration round; for now we assert that the SEA pipeline + * kernel-integration round; for now we assert that the kernel pipeline * itself runs cleanly to completion. */ -describe('SEA execution end-to-end', function e2eSuite() { +describe('kernel execution end-to-end', function e2eSuite() { const hostName = process.env.DATABRICKS_PECOTESTING_SERVER_HOSTNAME; const httpPath = process.env.DATABRICKS_PECOTESTING_HTTP_PATH; const token = process.env.DATABRICKS_PECOTESTING_TOKEN_PERSONAL; @@ -60,14 +60,14 @@ describe('SEA execution end-to-end', function e2eSuite() { } }); - it('opens a session, executes SELECT 1, and closes cleanly via SEA backend', async () => { + it('opens a session, executes SELECT 1, and closes cleanly via kernel backend', async () => { const client = new DBSQLClient(); await client.connect({ host: hostName as string, path: httpPath as string, token: token as string, - useSEA: true, + useKernel: true, } as ConnectionOptions & InternalConnectionOptions); const session = await client.openSession({ @@ -79,12 +79,12 @@ describe('SEA execution end-to-end', function e2eSuite() { const operation = await session.executeStatement('SELECT 1', {}); expect(operation).to.be.an('object'); // `IOperation.id` is the public-API observable identity for the - // returned operation. SeaOperationBackend generates a UUIDv4 for + // returned operation. KernelOperationBackend generates a UUIDv4 for // M0 until the napi binding surfaces the server statement id. expect(operation.id).to.be.a('string').and.have.length.greaterThan(0); - // M0 does not yet plumb fetchChunk through the SEA pipeline - // (sea-results owns that). We exercise the lifecycle: cancel is a + // M0 does not yet plumb fetchChunk through the kernel pipeline + // (kernel-results owns that). We exercise the lifecycle: cancel is a // no-op against a finished statement, close releases the kernel // handle. await operation.close(); @@ -100,7 +100,7 @@ describe('SEA execution end-to-end', function e2eSuite() { host: hostName as string, path: httpPath as string, token: token as string, - useSEA: true, + useKernel: true, } as ConnectionOptions & InternalConnectionOptions); // Sanity-check that supplying session-level Spark conf does not diff --git a/tests/e2e/sea/interval-duration-e2e.test.ts b/tests/e2e/kernel/interval-duration-e2e.test.ts similarity index 88% rename from tests/e2e/sea/interval-duration-e2e.test.ts rename to tests/e2e/kernel/interval-duration-e2e.test.ts index 877b7b9a..656642f5 100644 --- a/tests/e2e/sea/interval-duration-e2e.test.ts +++ b/tests/e2e/kernel/interval-duration-e2e.test.ts @@ -19,16 +19,16 @@ import { DBSQLClient } from '../../../lib'; import { ConnectionOptions } from '../../../lib/contracts/IDBSQLClient'; import { InternalConnectionOptions } from '../../../lib/contracts/InternalConnectionOptions'; -// Exercises the SeaArrowIpcDurationFix rewriter against a REAL kernel-produced +// Exercises the KernelArrowIpcDurationFix rewriter against a REAL kernel-produced // Arrow Duration buffer (Spark INTERVAL DAY-TIME surfaces as Arrow Duration, // type id 18, which apache-arrow@13 can't decode). Without the rewriter this // query throws `Unrecognized type: "Duration" (18)`, so a passing fetch proves // the hand-rolled FlatBuffer schema re-encode + Int64 splice-back ran. // -// On THIS layer (SEA execution + results, PR 2/3) the converter does not yet +// On THIS layer (kernel execution + results, PR 2/3) the converter does not yet // consume the `duration_unit` marker, so the value surfaces as a raw Int64 — // asserted here. The Phase-1 formatter that turns it into the thrift string -// "1 02:03:04.000000000" lands in PR 3/3 (#411), where SeaIntervalParity covers +// "1 02:03:04.000000000" lands in PR 3/3 (#411), where KernelIntervalParity covers // the formatted output. Requires the pecotesting secrets; skips otherwise. const DURATION_QUERY = "SELECT INTERVAL '1 02:03:04' DAY TO SECOND AS dt"; @@ -47,13 +47,13 @@ function readSecrets(): PecoSecrets | null { return { host, path, token }; } -async function fetchOneRow(useSEA: boolean, secrets: PecoSecrets): Promise> { +async function fetchOneRow(useKernel: boolean, secrets: PecoSecrets): Promise> { const client = new DBSQLClient(); await client.connect({ host: secrets.host, path: secrets.path, token: secrets.token, - useSEA, + useKernel, } as ConnectionOptions & InternalConnectionOptions); try { const session = await client.openSession(); @@ -73,7 +73,7 @@ async function fetchOneRow(useSEA: boolean, secrets: PecoSecrets): Promise { +async function kernelValues(sql: string, secrets: PecoSecrets): Promise { const client = new DBSQLClient(); - await client.connect({ ...secrets, useSEA: true } as ConnectionOptions & InternalConnectionOptions); + await client.connect({ ...secrets, useKernel: true } as ConnectionOptions & InternalConnectionOptions); try { const session = await client.openSession(); const op = await session.executeStatement(sql); @@ -53,7 +53,7 @@ async function seaValues(sql: string, secrets: PecoSecrets): Promise } } -describe('SEA INTERVAL edge cases end-to-end', function suite() { +describe('kernel INTERVAL edge cases end-to-end', function suite() { this.timeout(120_000); const secrets = readSecrets(); @@ -67,19 +67,19 @@ describe('SEA INTERVAL edge cases end-to-end', function suite() { } // Skip (not error) when the native binding isn't built/installed. try { - getSeaNative(); + getKernelNative(); } catch { self.skip(); } }); it('NULL INTERVAL DAY-TIME → null', async () => { - const values = await seaValues('SELECT CAST(NULL AS INTERVAL DAY TO SECOND) AS v', secrets as PecoSecrets); + const values = await kernelValues('SELECT CAST(NULL AS INTERVAL DAY TO SECOND) AS v', secrets as PecoSecrets); expect(values).to.deep.equal([null]); }); it('multi-row INTERVAL DAY-TIME batch formats every row', async () => { - const values = await seaValues( + const values = await kernelValues( "SELECT * FROM VALUES (INTERVAL '1' DAY), (INTERVAL '2 03:00:00' DAY TO SECOND), (INTERVAL '0' DAY) AS t(v)", secrets as PecoSecrets, ); diff --git a/tests/e2e/sea/logging-e2e.test.ts b/tests/e2e/kernel/logging-e2e.test.ts similarity index 88% rename from tests/e2e/sea/logging-e2e.test.ts rename to tests/e2e/kernel/logging-e2e.test.ts index 74f170e8..c1de5462 100644 --- a/tests/e2e/sea/logging-e2e.test.ts +++ b/tests/e2e/kernel/logging-e2e.test.ts @@ -14,14 +14,14 @@ // End-to-end proof that kernel (Rust) logs and Node-driver logs land in the // SAME `DBSQLLogger` sink — here, one file. Goes through the full public -// `DBSQLClient` surface (not the raw binding) so the `SeaBackend` → +// `DBSQLClient` surface (not the raw binding) so the `KernelBackend` → // `installKernelLogBridge` → napi `initKernelLogging` wiring is exercised. import { expect } from 'chai'; import fs from 'fs'; import os from 'os'; import path from 'path'; -import { tryGetSeaNative } from '../../../lib/sea/SeaNativeLoader'; +import { tryGetKernelNative } from '../../../lib/kernel/KernelNativeLoader'; import { DBSQLClient } from '../../../lib'; import DBSQLLogger from '../../../lib/DBSQLLogger'; import { LogLevel } from '../../../lib/contracts/IDBSQLLogger'; @@ -33,13 +33,13 @@ const delay = (ms: number) => setTimeout(resolve, ms); }); -describe('SEA — unified kernel + driver logging', function unifiedLogging() { +describe('kernel — unified kernel + driver logging', function unifiedLogging() { // Live-warehouse round-trip plus async log flush. this.timeout(60_000); - const binding = tryGetSeaNative(); + const binding = tryGetKernelNative(); if (binding === undefined) { - it.skip('SEA native binding not available on this platform'); + it.skip('kernel native binding not available on this platform'); return; } @@ -54,7 +54,7 @@ describe('SEA — unified kernel + driver logging', function unifiedLogging() { path: config.path, token: config.token, // Route through the kernel backend (internal opt-in flag). - ...({ useSEA: true } as InternalConnectionOptions), + ...({ useKernel: true } as InternalConnectionOptions), }); const session = await client.openSession(); const operation = await session.executeStatement('SELECT 1'); diff --git a/tests/e2e/sea/operation-lifecycle-e2e.test.ts b/tests/e2e/kernel/operation-lifecycle-e2e.test.ts similarity index 85% rename from tests/e2e/sea/operation-lifecycle-e2e.test.ts rename to tests/e2e/kernel/operation-lifecycle-e2e.test.ts index 5d529aca..3f9ec113 100644 --- a/tests/e2e/sea/operation-lifecycle-e2e.test.ts +++ b/tests/e2e/kernel/operation-lifecycle-e2e.test.ts @@ -13,35 +13,35 @@ // limitations under the License. /** - * End-to-end tests for the SEA operation lifecycle (cancel / close / - * finished) wired through `SeaOperationBackend`. + * End-to-end tests for the kernel operation lifecycle (cancel / close / + * finished) wired through `KernelOperationBackend`. * * The impl-execution feature has not yet wired - * `DBSQLClient.connect({ useSEA: true })` to dispatch into - * `SeaBackend`, so this test drives the lifecycle by: + * `DBSQLClient.connect({ useKernel: true })` to dispatch into + * `KernelBackend`, so this test drives the lifecycle by: * 1. Calling the napi `openSession(...)` free function directly to * get a kernel `Connection`. * 2. Calling `connection.executeStatement(...)` to get a napi * `Statement` handle. - * 3. Wrapping that handle in a `SeaOperationBackend` and exercising + * 3. Wrapping that handle in a `KernelOperationBackend` and exercising * its `cancel()` / `close()` / `waitUntilReady()` methods. * - * This mirrors how the eventual `SeaSessionBackend.executeStatement` + * This mirrors how the eventual `KernelSessionBackend.executeStatement` * call path will assemble the operation — we just inline the kernel * call here since the session backend is being built in parallel. * * Path note: the original task spec referenced - * `tests/integration/sea/operation-lifecycle-e2e.test.ts`. The + * `tests/integration/kernel/operation-lifecycle-e2e.test.ts`. The * existing project structure uses `tests/e2e/**` (with its own - * `.mocharc.js`), so this file lives under `tests/e2e/sea/` to be + * `.mocharc.js`), so this file lives under `tests/e2e/kernel/` to be * picked up by `npm run e2e` automatically. */ import { expect } from 'chai'; import IClientContext from '../../../lib/contracts/IClientContext'; import IDBSQLLogger, { LogLevel } from '../../../lib/contracts/IDBSQLLogger'; -import { getSeaNative } from '../../../lib/sea/SeaNativeLoader'; -import SeaOperationBackend from '../../../lib/sea/SeaOperationBackend'; +import { getKernelNative } from '../../../lib/kernel/KernelNativeLoader'; +import KernelOperationBackend from '../../../lib/kernel/KernelOperationBackend'; import OperationStateError, { OperationStateErrorCode } from '../../../lib/errors/OperationStateError'; // Minimal binding type shapes (mirrors the napi `index.d.ts`). @@ -89,7 +89,7 @@ function makeContext(): IClientContext { } as unknown as IClientContext; } -describe('SEA operation lifecycle — end-to-end', function suite() { +describe('kernel operation lifecycle — end-to-end', function suite() { // Live-warehouse tests can take >2s through warm-up; bump the // mocha default (2000ms) generously. The base `tests/e2e/.mocharc.js` // already sets 300s but we keep this explicit so the file is robust @@ -108,17 +108,17 @@ describe('SEA operation lifecycle — end-to-end', function suite() { return; } // Creds present but the native binding not built/installed (e.g. a CI - // runner without the .node) must SKIP, not error: probe getSeaNative() + // runner without the .node) must SKIP, not error: probe getKernelNative() // here so every test isn't an uncaught-throw at its first call. try { - getSeaNative(); + getKernelNative(); } catch { self.skip(); } }); - it('cancel() succeeds against a live SEA statement', async () => { - const binding = getSeaNative() as unknown as NativeBinding; + it('cancel() succeeds against a live kernel statement', async () => { + const binding = getKernelNative() as unknown as NativeBinding; const connection = await binding.openSession({ hostName: hostName as string, @@ -135,7 +135,7 @@ describe('SEA operation lifecycle — end-to-end', function suite() { statement = await connection.executeStatement('SELECT * FROM range(0, 100000000)', {}); expect(statement).to.be.an('object'); - const op = new SeaOperationBackend({ + const op = new KernelOperationBackend({ statement: statement as unknown as NativeStatement, context: makeContext(), }); @@ -160,7 +160,7 @@ describe('SEA operation lifecycle — end-to-end', function suite() { }); it('cancel mid-fetch — subsequent fetchChunk throws OperationStateError', async () => { - const binding = getSeaNative() as unknown as NativeBinding; + const binding = getKernelNative() as unknown as NativeBinding; const connection = await binding.openSession({ hostName: hostName as string, @@ -172,7 +172,7 @@ describe('SEA operation lifecycle — end-to-end', function suite() { try { statement = await connection.executeStatement('SELECT * FROM range(0, 100000000)', {}); - const op = new SeaOperationBackend({ + const op = new KernelOperationBackend({ statement: statement as unknown as NativeStatement, context: makeContext(), }); @@ -202,8 +202,8 @@ describe('SEA operation lifecycle — end-to-end', function suite() { } }); - it('close() succeeds against a SEA statement and is idempotent', async () => { - const binding = getSeaNative() as unknown as NativeBinding; + it('close() succeeds against a kernel statement and is idempotent', async () => { + const binding = getKernelNative() as unknown as NativeBinding; const connection = await binding.openSession({ hostName: hostName as string, @@ -214,7 +214,7 @@ describe('SEA operation lifecycle — end-to-end', function suite() { try { const statement = await connection.executeStatement('SELECT 1', {}); - const op = new SeaOperationBackend({ + const op = new KernelOperationBackend({ statement: statement as unknown as NativeStatement, context: makeContext(), }); @@ -233,7 +233,7 @@ describe('SEA operation lifecycle — end-to-end', function suite() { }); it('finished() resolves immediately and fires the progress callback', async () => { - const binding = getSeaNative() as unknown as NativeBinding; + const binding = getKernelNative() as unknown as NativeBinding; const connection = await binding.openSession({ hostName: hostName as string, @@ -245,7 +245,7 @@ describe('SEA operation lifecycle — end-to-end', function suite() { try { statement = await connection.executeStatement('SELECT 1', {}); - const op = new SeaOperationBackend({ + const op = new KernelOperationBackend({ statement: statement as unknown as NativeStatement, context: makeContext(), }); diff --git a/tests/e2e/sea/results-e2e.test.ts b/tests/e2e/kernel/results-e2e.test.ts similarity index 87% rename from tests/e2e/sea/results-e2e.test.ts rename to tests/e2e/kernel/results-e2e.test.ts index 497889c5..5f5969d7 100644 --- a/tests/e2e/sea/results-e2e.test.ts +++ b/tests/e2e/kernel/results-e2e.test.ts @@ -44,13 +44,13 @@ function readSecrets(): PecoSecrets | null { return { host, path, token }; } -async function fetchProbeRows(useSEA: boolean, secrets: PecoSecrets): Promise>> { +async function fetchProbeRows(useKernel: boolean, secrets: PecoSecrets): Promise>> { const client = new DBSQLClient(); await client.connect({ host: secrets.host, path: secrets.path, token: secrets.token, - useSEA, + useKernel, } as ConnectionOptions & InternalConnectionOptions); try { const session = await client.openSession(); @@ -73,7 +73,7 @@ async function fetchProbeRows(useSEA: boolean, secrets: PecoSecrets): Promise { + it('kernel backend returns one row with expected columns', async () => { const rows = await fetchProbeRows(true, secrets as PecoSecrets); expect(rows.length).to.equal(1); const row = rows[0]; @@ -120,9 +120,9 @@ describe('SEA results end-to-end (pecotesting parity gate)', function suite() { expect(Number(row.d)).to.equal(1.5); }); - it('Thrift and SEA produce byte-identical rows for the probe query (parity gate)', async () => { - const seaRows = await fetchProbeRows(true, secrets as PecoSecrets); + it('Thrift and kernel produce byte-identical rows for the probe query (parity gate)', async () => { + const kernelRows = await fetchProbeRows(true, secrets as PecoSecrets); const thriftRows = await fetchProbeRows(false, secrets as PecoSecrets); - expect(seaRows.map(canonical)).to.deep.equal(thriftRows.map(canonical)); + expect(kernelRows.map(canonical)).to.deep.equal(thriftRows.map(canonical)); }); }); diff --git a/tests/unit/DBSQLClient.test.ts b/tests/unit/DBSQLClient.test.ts index 81d41f2e..ab45251b 100644 --- a/tests/unit/DBSQLClient.test.ts +++ b/tests/unit/DBSQLClient.test.ts @@ -118,19 +118,19 @@ describe('DBSQLClient.connect', () => { logSpy.restore(); }); - it('useSEA: true routes to SeaBackend and leaves `backend` unset when connect() throws', async () => { + it('useKernel: true routes to KernelBackend and leaves `backend` unset when connect() throws', async () => { const client = new DBSQLClient(); - // `useSEA` is on a non-exported InternalConnectionOptions; cast through any. - // An empty token makes the real SeaBackend reject during connect() (auth + // `useKernel` is on a non-exported InternalConnectionOptions; cast through any. + // An empty token makes the real KernelBackend reject during connect() (auth // validation); where the native binding is absent (e.g. CI, which does not // build it) construction throws even earlier. Either way connect() must // reject, so we can assert the partial-init guard leaves `backend` unset. - const seaOptions = { ...connectOptions, token: '', useSEA: true } as any; + const kernelOptions = { ...connectOptions, token: '', useKernel: true } as any; try { - await client.connect(seaOptions); - expect.fail('SeaBackend connect() should reject (empty PAT / absent native binding)'); + await client.connect(kernelOptions); + expect.fail('KernelBackend connect() should reject (empty PAT / absent native binding)'); } catch (error) { if (error instanceof AssertionError || !(error instanceof Error)) { throw error; @@ -141,7 +141,7 @@ describe('DBSQLClient.connect', () => { // The partial-init guard (L2 fix) means backend stays undefined after a // failed connect, so the next openSession surfaces "not connected" rather - // than the SeaBackend's own connect/auth error. + // than the KernelBackend's own connect/auth error. expect((client as any).backend).to.equal(undefined); try { diff --git a/tests/unit/sea/SeaArrowIpc.test.ts b/tests/unit/kernel/KernelArrowIpc.test.ts similarity index 88% rename from tests/unit/sea/SeaArrowIpc.test.ts rename to tests/unit/kernel/KernelArrowIpc.test.ts index e93fa8c1..ab27c8b1 100644 --- a/tests/unit/sea/SeaArrowIpc.test.ts +++ b/tests/unit/kernel/KernelArrowIpc.test.ts @@ -14,13 +14,13 @@ import { expect } from 'chai'; import { tableFromArrays, RecordBatchStreamWriter } from 'apache-arrow'; -import { countRowsInIpc, decodeIpcSchema, patchIpcBytes } from '../../../lib/sea/SeaArrowIpc'; -import { rewriteDurationToInt64 } from '../../../lib/sea/SeaArrowIpcDurationFix'; +import { countRowsInIpc, decodeIpcSchema, patchIpcBytes } from '../../../lib/kernel/KernelArrowIpc'; +import { rewriteDurationToInt64 } from '../../../lib/kernel/KernelArrowIpcDurationFix'; import HiveDriverError from '../../../lib/errors/HiveDriverError'; -// Hermetic coverage for the SEA Arrow-IPC layer. apache-arrow@13 cannot +// Hermetic coverage for the kernel Arrow-IPC layer. apache-arrow@13 cannot // construct a `Duration` column, so the Duration-positive rewrite path is -// covered by the live e2e (tests/e2e/sea/interval-duration-e2e.test.ts). +// covered by the live e2e (tests/e2e/kernel/interval-duration-e2e.test.ts). // These tests pin everything reachable without a warehouse: the IPC // framing walk, the no-op / malformed-input handling, the cheap // row-count path, and the schema-decode guard. @@ -46,7 +46,7 @@ function makeSchemaOnlyStream(): Buffer { return Buffer.from(writer.toUint8Array(true)); } -describe('SeaArrowIpc.countRowsInIpc', () => { +describe('KernelArrowIpc.countRowsInIpc', () => { it('sums RecordBatch row counts across messages', () => { const ipc = makeIpcStream([ [1, 2, 3], @@ -64,7 +64,7 @@ describe('SeaArrowIpc.countRowsInIpc', () => { }); }); -describe('SeaArrowIpc.rewriteDurationToInt64 (no-Duration / malformed paths)', () => { +describe('KernelArrowIpc.rewriteDurationToInt64 (no-Duration / malformed paths)', () => { it('is a no-op for a stream with no Duration field (returns input unchanged)', () => { const ipc = makeIpcStream([[1, 2, 3]]); const out = rewriteDurationToInt64(ipc); @@ -90,7 +90,7 @@ describe('SeaArrowIpc.rewriteDurationToInt64 (no-Duration / malformed paths)', ( }); }); -describe('SeaArrowIpc.decodeIpcSchema', () => { +describe('KernelArrowIpc.decodeIpcSchema', () => { it('decodes the schema of a normal stream', () => { const schema = decodeIpcSchema(makeIpcStream([[1]])); expect(schema.fields.map((f) => f.name)).to.deep.equal(['a']); diff --git a/tests/unit/sea/SeaIntervalParity.test.ts b/tests/unit/kernel/KernelIntervalParity.test.ts similarity index 91% rename from tests/unit/sea/SeaIntervalParity.test.ts rename to tests/unit/kernel/KernelIntervalParity.test.ts index c0cab289..ed24ae47 100644 --- a/tests/unit/sea/SeaIntervalParity.test.ts +++ b/tests/unit/kernel/KernelIntervalParity.test.ts @@ -9,20 +9,20 @@ /** * TDD harness for the round-2 INTERVAL parity fix. * - * Verifies that the SEA path renders the exact thrift wire string for + * Verifies that the kernel path renders the exact thrift wire string for * INTERVAL YEAR-MONTH and INTERVAL DAY-TIME columns, regardless of * whether the kernel emits the value as native Arrow `Interval` or * native Arrow `Duration` (the latter is transparently rewritten to - * `Int64` by `lib/sea/SeaArrowIpcDurationFix.ts` because `apache-arrow@13` + * `Int64` by `lib/kernel/KernelArrowIpcDurationFix.ts` because `apache-arrow@13` * predates the `Duration` type id). * * Reference failure modes (round 5 testing): * - YEAR-MONTH: * thrift → `"1-2"` (string) - * SEA pre-fix → `{"0":1,"1":2}` (Int32Array surfaced as struct) + * kernel pre-fix → `{"0":1,"1":2}` (Int32Array surfaced as struct) * - DAY-TIME: * thrift → `"1 02:03:04.000000000"` (string) - * SEA pre-fix → throws `Unrecognized type: "Duration" (18)` on schema decode + * kernel pre-fix → throws `Unrecognized type: "Duration" (18)` on schema decode * * Both modes must now produce byte-identical thrift strings. */ @@ -59,11 +59,11 @@ import { Duration as FbDuration } from 'apache-arrow/fb/duration'; // eslint-disable-next-line import/no-internal-modules import { TimeUnit as FbTimeUnit } from 'apache-arrow/fb/time-unit'; -import SeaOperationBackend from '../../../lib/sea/SeaOperationBackend'; +import KernelOperationBackend from '../../../lib/kernel/KernelOperationBackend'; import ClientContextStub from '../.stubs/ClientContextStub'; import HiveDriverError from '../../../lib/errors/HiveDriverError'; import { DURATION_UNIT_METADATA_KEY as CONVERTER_DURATION_KEY } from '../../../lib/result/ArrowResultConverter'; -import { DURATION_UNIT_METADATA_KEY as REWRITER_DURATION_KEY } from '../../../lib/sea/SeaArrowIpcDurationFix'; +import { DURATION_UNIT_METADATA_KEY as REWRITER_DURATION_KEY } from '../../../lib/kernel/KernelArrowIpcDurationFix'; // --------------------------------------------------------------------------- // Test helpers. @@ -245,7 +245,7 @@ function buildDurationIpc( // Tests. // --------------------------------------------------------------------------- -describe('SeaOperationBackend — INTERVAL parity with thrift', () => { +describe('KernelOperationBackend — INTERVAL parity with thrift', () => { it('YEAR-MONTH via native Arrow Interval[YearMonth] → "Y-M"', async () => { // Arrow `Interval[YearMonth]` carries a single int32 total-months // value. apache-arrow surfaces it as Int32Array(2) via the @@ -260,7 +260,7 @@ describe('SeaOperationBackend — INTERVAL parity with thrift', () => { const dataIpc = ipcFromColumns(schema, { iv: Int32Array.from([14]) }); const stub = new StatementStub(schemaIpc, [dataIpc]); - const backend = new SeaOperationBackend({ statement: stub, context: new ClientContextStub() }); + const backend = new KernelOperationBackend({ statement: stub, context: new ClientContextStub() }); const rows = await backend.fetchChunk({ limit: 100 }); expect(rows).to.have.length(1); expect((rows[0] as any).iv).to.equal('1-2'); @@ -275,7 +275,7 @@ describe('SeaOperationBackend — INTERVAL parity with thrift', () => { const dataIpc = ipcFromColumns(schema, { iv: Int32Array.from([-14]) }); const stub = new StatementStub(schemaIpc, [dataIpc]); - const backend = new SeaOperationBackend({ statement: stub, context: new ClientContextStub() }); + const backend = new KernelOperationBackend({ statement: stub, context: new ClientContextStub() }); const rows = await backend.fetchChunk({ limit: 100 }); expect(rows).to.have.length(1); expect((rows[0] as any).iv).to.equal('-1-2'); @@ -288,7 +288,7 @@ describe('SeaOperationBackend — INTERVAL parity with thrift', () => { const schemaIpc = ipcWithDurationSchema('iv', FbTimeUnit.MICROSECOND, 'INTERVAL'); const stub = new StatementStub(schemaIpc, [ipc]); - const backend = new SeaOperationBackend({ statement: stub, context: new ClientContextStub() }); + const backend = new KernelOperationBackend({ statement: stub, context: new ClientContextStub() }); const rows = await backend.fetchChunk({ limit: 100 }); expect(rows).to.have.length(1); expect((rows[0] as any).iv).to.equal('1 02:03:04.000000000'); @@ -301,7 +301,7 @@ describe('SeaOperationBackend — INTERVAL parity with thrift', () => { const schemaIpc = ipcWithDurationSchema('iv', FbTimeUnit.NANOSECOND, 'INTERVAL'); const stub = new StatementStub(schemaIpc, [ipc]); - const backend = new SeaOperationBackend({ statement: stub, context: new ClientContextStub() }); + const backend = new KernelOperationBackend({ statement: stub, context: new ClientContextStub() }); const rows = await backend.fetchChunk({ limit: 100 }); expect(rows).to.have.length(1); expect((rows[0] as any).iv).to.equal('1 02:03:04.123456789'); @@ -314,7 +314,7 @@ describe('SeaOperationBackend — INTERVAL parity with thrift', () => { const schemaIpc = ipcWithDurationSchema('iv', FbTimeUnit.MILLISECOND, 'INTERVAL'); const stub = new StatementStub(schemaIpc, [ipc]); - const backend = new SeaOperationBackend({ statement: stub, context: new ClientContextStub() }); + const backend = new KernelOperationBackend({ statement: stub, context: new ClientContextStub() }); const rows = await backend.fetchChunk({ limit: 100 }); expect(rows).to.have.length(1); expect((rows[0] as any).iv).to.equal('1 02:03:04.000000000'); @@ -327,7 +327,7 @@ describe('SeaOperationBackend — INTERVAL parity with thrift', () => { const schemaIpc = ipcWithDurationSchema('iv', FbTimeUnit.SECOND, 'INTERVAL'); const stub = new StatementStub(schemaIpc, [ipc]); - const backend = new SeaOperationBackend({ statement: stub, context: new ClientContextStub() }); + const backend = new KernelOperationBackend({ statement: stub, context: new ClientContextStub() }); const rows = await backend.fetchChunk({ limit: 100 }); expect(rows).to.have.length(1); expect((rows[0] as any).iv).to.equal('1 02:03:04.000000000'); @@ -345,7 +345,7 @@ describe('SeaOperationBackend — INTERVAL parity with thrift', () => { const dataIpc = ipcFromColumns(schema, { iv: [Int32Array.from([1, 1000])] }); const stub = new StatementStub(schemaIpc, [dataIpc]); - const backend = new SeaOperationBackend({ statement: stub, context: new ClientContextStub() }); + const backend = new KernelOperationBackend({ statement: stub, context: new ClientContextStub() }); let thrown: unknown; try { await backend.fetchChunk({ limit: 100 }); @@ -361,7 +361,7 @@ describe('SeaOperationBackend — INTERVAL parity with thrift', () => { const schemaIpc = ipcWithDurationSchema('iv', FbTimeUnit.MICROSECOND, 'INTERVAL'); const stub = new StatementStub(schemaIpc, [ipc]); - const backend = new SeaOperationBackend({ statement: stub, context: new ClientContextStub() }); + const backend = new KernelOperationBackend({ statement: stub, context: new ClientContextStub() }); const rows = await backend.fetchChunk({ limit: 100 }); expect(rows).to.have.length(1); expect((rows[0] as any).iv).to.equal('0 00:00:00.000000000'); @@ -374,7 +374,7 @@ describe('SeaOperationBackend — INTERVAL parity with thrift', () => { const schemaIpc = ipcWithDurationSchema('iv', FbTimeUnit.MICROSECOND, 'INTERVAL'); const stub = new StatementStub(schemaIpc, [ipc]); - const backend = new SeaOperationBackend({ statement: stub, context: new ClientContextStub() }); + const backend = new KernelOperationBackend({ statement: stub, context: new ClientContextStub() }); const rows = await backend.fetchChunk({ limit: 100 }); expect(rows).to.have.length(1); expect((rows[0] as any).iv).to.equal('-1 02:03:04.000000000'); @@ -400,7 +400,7 @@ describe('SeaOperationBackend — INTERVAL parity with thrift', () => { const schemaIpc = ipcWithDurationSchema('iv', FbTimeUnit.MICROSECOND, 'INTERVAL'); const stub = new StatementStub(schemaIpc, [ipc]); - const backend = new SeaOperationBackend({ statement: stub, context: new ClientContextStub() }); + const backend = new KernelOperationBackend({ statement: stub, context: new ClientContextStub() }); // Round-trip the metadata to confirm we synthesise the right TTypeId. // Interval columns are surfaced as STRING_TYPE — matching the Thrift @@ -429,7 +429,7 @@ describe('SeaOperationBackend — INTERVAL parity with thrift', () => { const dataIpc = ipcFromColumns(schema, { iv: Int32Array.from([-1]) }); const stub = new StatementStub(schemaIpc, [dataIpc]); - const backend = new SeaOperationBackend({ statement: stub, context: new ClientContextStub() }); + const backend = new KernelOperationBackend({ statement: stub, context: new ClientContextStub() }); const rows = await backend.fetchChunk({ limit: 100 }); expect(rows).to.have.length(1); expect((rows[0] as any).iv).to.equal('-0-1'); @@ -437,7 +437,7 @@ describe('SeaOperationBackend — INTERVAL parity with thrift', () => { it('the duration_unit metadata key is identical in the rewriter and the converter', () => { // Both modules declare the key independently (the converter avoids a - // compile-time dependency on lib/sea). Pin them equal so a rename/typo in + // compile-time dependency on lib/kernel). Pin them equal so a rename/typo in // one doesn't silently turn the converter's duration branch into a no-op. expect(CONVERTER_DURATION_KEY).to.equal(REWRITER_DURATION_KEY); expect(CONVERTER_DURATION_KEY).to.equal('databricks.arrow.duration_unit'); diff --git a/tests/unit/sea/SeaOperationBackend.test.ts b/tests/unit/kernel/KernelOperationBackend.test.ts similarity index 94% rename from tests/unit/sea/SeaOperationBackend.test.ts rename to tests/unit/kernel/KernelOperationBackend.test.ts index ada6616d..2980ecce 100644 --- a/tests/unit/sea/SeaOperationBackend.test.ts +++ b/tests/unit/kernel/KernelOperationBackend.test.ts @@ -36,12 +36,12 @@ import { vectorFromArray, } from 'apache-arrow'; -import SeaOperationBackend from '../../../lib/sea/SeaOperationBackend'; +import KernelOperationBackend from '../../../lib/kernel/KernelOperationBackend'; import ClientContextStub from '../.stubs/ClientContextStub'; // Minimal stub of the napi `Statement` surface that emits a precomputed // Arrow IPC payload per `fetchNextBatch()` call. Used to feed -// `SeaOperationBackend` synthetic batches that mirror the kernel's +// `KernelOperationBackend` synthetic batches that mirror the kernel's // per-batch IPC stream contract (`schema header + 1 record-batch // message`) without loading the native binding. class StatementStub { @@ -97,7 +97,7 @@ class StatementStub { } } -// Helper: attach `databricks.type_name` to a field so the SEA Thrift +// Helper: attach `databricks.type_name` to a field so the kernel Thrift // schema synthesiser can resolve the TTypeId (matches kernel behaviour // at `src/reader/mod.rs:476-504`). function withTypeName(field: T, typeName: string): T { @@ -140,7 +140,7 @@ function ipcSchemaOnly(schema: Schema): Buffer { return Buffer.from(tableToIPC(table, 'stream')); } -describe('SeaOperationBackend — M0 datatype round-trip via napi → ArrowResultConverter', () => { +describe('KernelOperationBackend — M0 datatype round-trip via napi → ArrowResultConverter', () => { it('passes M0 primitive datatypes through the same converter the thrift path uses', async () => { // One row per M0 primitive type with a kernel-style metadata tag on // each field. Decimal carries a real scale (2) so the converter's @@ -189,7 +189,7 @@ describe('SeaOperationBackend — M0 datatype round-trip via napi → ArrowResul }); const stub = new StatementStub(schemaIpc, [dataIpc]); - const backend = new SeaOperationBackend({ + const backend = new KernelOperationBackend({ statement: stub, context: new ClientContextStub(), }); @@ -245,7 +245,7 @@ describe('SeaOperationBackend — M0 datatype round-trip via napi → ArrowResul }); const stub = new StatementStub(strSchemaIpc, [strDataIpc]); - const backend = new SeaOperationBackend({ + const backend = new KernelOperationBackend({ statement: stub, context: new ClientContextStub(), }); @@ -264,7 +264,7 @@ describe('SeaOperationBackend — M0 datatype round-trip via napi → ArrowResul const batch2 = ipcFromColumns(schema, { x: [3] }); const stub = new StatementStub(schemaIpc, [batch1, batch2]); - const backend = new SeaOperationBackend({ + const backend = new KernelOperationBackend({ statement: stub, context: new ClientContextStub(), }); @@ -278,7 +278,7 @@ describe('SeaOperationBackend — M0 datatype round-trip via napi → ArrowResul const schema = new Schema([withTypeName(new Field('x', new Int32(), true), 'INT')]); const schemaIpc = ipcSchemaOnly(schema); const stub = new StatementStub(schemaIpc, []); - const backend = new SeaOperationBackend({ statement: stub, context: new ClientContextStub() }); + const backend = new KernelOperationBackend({ statement: stub, context: new ClientContextStub() }); await backend.cancel(); expect(stub.cancelled).to.equal(true); await backend.close(); diff --git a/tests/unit/sea/_helpers/fakeBinding.ts b/tests/unit/kernel/_helpers/fakeBinding.ts similarity index 81% rename from tests/unit/sea/_helpers/fakeBinding.ts rename to tests/unit/kernel/_helpers/fakeBinding.ts index 16c101de..3284f2e0 100644 --- a/tests/unit/sea/_helpers/fakeBinding.ts +++ b/tests/unit/kernel/_helpers/fakeBinding.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { SeaNativeBinding, SeaConnection } from '../../../../lib/sea/SeaNativeLoader'; +import { KernelNativeBinding, KernelConnection } from '../../../../lib/kernel/KernelNativeLoader'; import IClientContext from '../../../../lib/contracts/IClientContext'; export interface RecordedCall { @@ -21,7 +21,7 @@ export interface RecordedCall { } /** - * Minimal `IClientContext` for SEA backend tests. `SeaBackend` requires a + * Minimal `IClientContext` for kernel backend tests. `KernelBackend` requires a * context (it threads logger/config into the session → operation chain), so * even auth-routing tests that never log must supply one. Only `getLogger` * is wired (to a no-op); the rest throw if unexpectedly reached. @@ -40,14 +40,14 @@ export function makeFakeContext(): IClientContext { } export interface FakeBinding { - binding: SeaNativeBinding; + binding: KernelNativeBinding; calls: RecordedCall[]; } /** - * Build a fake `SeaNativeBinding` that records every `openSession` call + * Build a fake `KernelNativeBinding` that records every `openSession` call * and returns a `Connection` whose `close()` is also recorded. Shared - * across the SEA auth unit-test files (PAT / M2M / U2M / future flows) + * across the kernel auth unit-test files (PAT / M2M / U2M / future flows) * so the closure shape lives in exactly one place. * * No real native code runs — the fake is structural-typing-only. @@ -71,13 +71,13 @@ export function makeFakeBinding(): FakeBinding { version() { return 'fake-binding'; }, - async openSession(opts: Parameters[0]) { + async openSession(opts: Parameters[0]) { calls.push({ method: 'openSession', args: [opts] }); - return fakeConnection as unknown as SeaConnection; + return fakeConnection as unknown as KernelConnection; }, Connection: function FakeConnection() {}, Statement: function FakeStatement() {}, - } as unknown as SeaNativeBinding; + } as unknown as KernelNativeBinding; return { binding, calls }; } diff --git a/tests/unit/sea/auth-edge-cases.test.ts b/tests/unit/kernel/auth-edge-cases.test.ts similarity index 87% rename from tests/unit/sea/auth-edge-cases.test.ts rename to tests/unit/kernel/auth-edge-cases.test.ts index 9f0928bb..371ac22f 100644 --- a/tests/unit/sea/auth-edge-cases.test.ts +++ b/tests/unit/kernel/auth-edge-cases.test.ts @@ -13,14 +13,14 @@ // limitations under the License. import { expect } from 'chai'; -import SeaBackend from '../../../lib/sea/SeaBackend'; -import { buildSeaConnectionOptions } from '../../../lib/sea/SeaAuth'; +import KernelBackend from '../../../lib/kernel/KernelBackend'; +import { buildKernelConnectionOptions } from '../../../lib/kernel/KernelAuth'; import { ConnectionOptions } from '../../../lib/contracts/IDBSQLClient'; import AuthenticationError from '../../../lib/errors/AuthenticationError'; import HiveDriverError from '../../../lib/errors/HiveDriverError'; import { makeFakeBinding, makeFakeContext } from './_helpers/fakeBinding'; -describe('SeaAuth — edge cases (input validation + ambiguity)', () => { +describe('KernelAuth — edge cases (input validation + ambiguity)', () => { describe('whitespace-only and reserved-literal credentials are rejected', () => { it('rejects whitespace-only PAT', () => { const opts: ConnectionOptions = { @@ -29,7 +29,7 @@ describe('SeaAuth — edge cases (input validation + ambiguity)', () => { token: ' \t ', }; - expect(() => buildSeaConnectionOptions(opts)).to.throw(AuthenticationError, /non-empty PAT/); + expect(() => buildKernelConnectionOptions(opts)).to.throw(AuthenticationError, /non-empty PAT/); }); it('rejects literal "undefined" as PAT (buggy shell-export hazard)', () => { @@ -39,7 +39,7 @@ describe('SeaAuth — edge cases (input validation + ambiguity)', () => { token: 'undefined', }; - expect(() => buildSeaConnectionOptions(opts)).to.throw(AuthenticationError, /non-empty PAT/); + expect(() => buildKernelConnectionOptions(opts)).to.throw(AuthenticationError, /non-empty PAT/); }); it('rejects literal "null" as PAT', () => { @@ -49,7 +49,7 @@ describe('SeaAuth — edge cases (input validation + ambiguity)', () => { token: 'null', }; - expect(() => buildSeaConnectionOptions(opts)).to.throw(AuthenticationError, /non-empty PAT/); + expect(() => buildKernelConnectionOptions(opts)).to.throw(AuthenticationError, /non-empty PAT/); }); it('rejects mixed-case "UNDEFINED" / "Null" / "NULL" as PAT (case-insensitive)', () => { @@ -60,7 +60,7 @@ describe('SeaAuth — edge cases (input validation + ambiguity)', () => { token: reserved, }; - expect(() => buildSeaConnectionOptions(opts), `for token=${reserved}`).to.throw( + expect(() => buildKernelConnectionOptions(opts), `for token=${reserved}`).to.throw( AuthenticationError, /non-empty PAT/, ); @@ -82,7 +82,7 @@ describe('SeaAuth — edge cases (input validation + ambiguity)', () => { oauthClientSecret: 'NULL', }; - expect(() => buildSeaConnectionOptions(opts)).to.throw( + expect(() => buildKernelConnectionOptions(opts)).to.throw( AuthenticationError, /oauthClientSecret.*non-empty.*OAuth M2M/, ); @@ -97,7 +97,7 @@ describe('SeaAuth — edge cases (input validation + ambiguity)', () => { oauthClientSecret: 'dose-fake-secret', }; - expect(() => buildSeaConnectionOptions(opts)).to.throw(AuthenticationError, /oauthClientId.*required/); + expect(() => buildKernelConnectionOptions(opts)).to.throw(AuthenticationError, /oauthClientId.*required/); }); it('rejects whitespace-only oauthClientSecret with AuthenticationError when oauthClientId is set (M2M intent)', () => { @@ -109,7 +109,7 @@ describe('SeaAuth — edge cases (input validation + ambiguity)', () => { oauthClientSecret: '\n\t', }; - expect(() => buildSeaConnectionOptions(opts)).to.throw( + expect(() => buildKernelConnectionOptions(opts)).to.throw( AuthenticationError, /oauthClientSecret.*non-empty.*OAuth M2M/, ); @@ -124,7 +124,7 @@ describe('SeaAuth — edge cases (input validation + ambiguity)', () => { oauthClientSecret: 'dose-fake-secret', }; - expect(() => buildSeaConnectionOptions(opts)).to.throw(AuthenticationError, /oauthClientId.*required/); + expect(() => buildKernelConnectionOptions(opts)).to.throw(AuthenticationError, /oauthClientId.*required/); }); it('rejects literal "undefined" as oauthClientSecret with AuthenticationError when id is set (M2M intent)', () => { @@ -136,7 +136,7 @@ describe('SeaAuth — edge cases (input validation + ambiguity)', () => { oauthClientSecret: 'undefined', }; - expect(() => buildSeaConnectionOptions(opts)).to.throw( + expect(() => buildKernelConnectionOptions(opts)).to.throw( AuthenticationError, /oauthClientSecret.*non-empty.*OAuth M2M/, ); @@ -162,7 +162,7 @@ describe('SeaAuth — edge cases (input validation + ambiguity)', () => { oauthClientSecret: '', }; - expect(() => buildSeaConnectionOptions(opts)).to.throw( + expect(() => buildKernelConnectionOptions(opts)).to.throw( AuthenticationError, /oauthClientSecret.*non-empty.*OAuth M2M/, ); @@ -183,7 +183,7 @@ describe('SeaAuth — edge cases (input validation + ambiguity)', () => { oauthClientId: ' ', } as unknown as ConnectionOptions; - expect(() => buildSeaConnectionOptions(opts)).to.throw( + expect(() => buildKernelConnectionOptions(opts)).to.throw( HiveDriverError, /oauthClientId.*not supported on the OAuth U2M flow/, ); @@ -201,7 +201,7 @@ describe('SeaAuth — edge cases (input validation + ambiguity)', () => { oauthClientId: 'client-uuid', } as any; - expect(() => buildSeaConnectionOptions(opts)).to.throw( + expect(() => buildKernelConnectionOptions(opts)).to.throw( HiveDriverError, /cannot supply both `token` and `oauthClientId/, ); @@ -217,7 +217,7 @@ describe('SeaAuth — edge cases (input validation + ambiguity)', () => { oauthClientSecret: 'dose-fake-secret', } as any; - expect(() => buildSeaConnectionOptions(opts)).to.throw( + expect(() => buildKernelConnectionOptions(opts)).to.throw( HiveDriverError, /cannot supply both `token` and `oauthClientId/, ); @@ -234,7 +234,7 @@ describe('SeaAuth — edge cases (input validation + ambiguity)', () => { token: 'dapi-fake-pat', } as any; - expect(() => buildSeaConnectionOptions(opts)).to.throw( + expect(() => buildKernelConnectionOptions(opts)).to.throw( HiveDriverError, /cannot supply `token` alongside `authType: 'databricks-oauth'`/, ); @@ -250,7 +250,7 @@ describe('SeaAuth — edge cases (input validation + ambiguity)', () => { token: 'dapi-fake-pat', } as any; - expect(() => buildSeaConnectionOptions(opts)).to.throw( + expect(() => buildKernelConnectionOptions(opts)).to.throw( HiveDriverError, /cannot supply `token` alongside `authType: 'databricks-oauth'`/, ); @@ -268,7 +268,7 @@ describe('SeaAuth — edge cases (input validation + ambiguity)', () => { oauthClientSecret: '', }; - const native = buildSeaConnectionOptions(opts); + const native = buildKernelConnectionOptions(opts); expect(native.authMode).to.equal('OAuthU2m'); }); @@ -280,7 +280,7 @@ describe('SeaAuth — edge cases (input validation + ambiguity)', () => { oauthClientSecret: ' \t ', }; - const native = buildSeaConnectionOptions(opts); + const native = buildKernelConnectionOptions(opts); expect(native.authMode).to.equal('OAuthU2m'); }); @@ -292,7 +292,7 @@ describe('SeaAuth — edge cases (input validation + ambiguity)', () => { oauthClientSecret: 'undefined', }; - const native = buildSeaConnectionOptions(opts); + const native = buildKernelConnectionOptions(opts); expect(native.authMode).to.equal('OAuthU2m'); }); }); @@ -308,7 +308,7 @@ describe('SeaAuth — edge cases (input validation + ambiguity)', () => { azureTenantId: undefined, }; - const native = buildSeaConnectionOptions(opts); + const native = buildKernelConnectionOptions(opts); expect(native.authMode).to.equal('OAuthM2m'); }); @@ -322,7 +322,7 @@ describe('SeaAuth — edge cases (input validation + ambiguity)', () => { useDatabricksOAuthInAzure: false, }; - const native = buildSeaConnectionOptions(opts); + const native = buildKernelConnectionOptions(opts); expect(native.authMode).to.equal('OAuthM2m'); }); @@ -334,13 +334,13 @@ describe('SeaAuth — edge cases (input validation + ambiguity)', () => { azureTenantId: undefined, }; - const native = buildSeaConnectionOptions(opts); + const native = buildKernelConnectionOptions(opts); expect(native.authMode).to.equal('OAuthU2m'); }); }); }); -describe('SeaBackend — kernel error envelope decoding (DA-F1)', () => { +describe('KernelBackend — kernel error envelope decoding (DA-F1)', () => { /** * Build a fake binding whose `openSession` rejects with the verbatim * `__databricks_error__:{...}` envelope shape the napi binding's @@ -366,7 +366,7 @@ describe('SeaBackend — kernel error envelope decoding (DA-F1)', () => { const binding = bindingRejectingWith( '{"code":"Unauthenticated","message":"OAuth M2M token exchange failed: invalid_client"}', ); - const backend = new SeaBackend({ nativeBinding: binding, context: makeFakeContext() }); + const backend = new KernelBackend({ nativeBinding: binding, context: makeFakeContext() }); await backend.connect(validConnectArgs); let caught: unknown; @@ -383,7 +383,7 @@ describe('SeaBackend — kernel error envelope decoding (DA-F1)', () => { const binding = bindingRejectingWith( '{"code":"NetworkError","message":"OIDC discovery failed: connection refused"}', ); - const backend = new SeaBackend({ nativeBinding: binding, context: makeFakeContext() }); + const backend = new KernelBackend({ nativeBinding: binding, context: makeFakeContext() }); await backend.connect(validConnectArgs); let caught: unknown; @@ -398,7 +398,7 @@ describe('SeaBackend — kernel error envelope decoding (DA-F1)', () => { it('preserves SQLSTATE on the decoded error when present', async () => { const binding = bindingRejectingWith('{"code":"Unauthenticated","message":"forbidden","sqlState":"28000"}'); - const backend = new SeaBackend({ nativeBinding: binding, context: makeFakeContext() }); + const backend = new KernelBackend({ nativeBinding: binding, context: makeFakeContext() }); await backend.connect(validConnectArgs); let caught: unknown; @@ -416,7 +416,7 @@ describe('SeaBackend — kernel error envelope decoding (DA-F1)', () => { binding.openSession = (async () => { throw new Error('openSession: `token` is required for the requested auth mode'); }) as typeof binding.openSession; - const backend = new SeaBackend({ nativeBinding: binding, context: makeFakeContext() }); + const backend = new KernelBackend({ nativeBinding: binding, context: makeFakeContext() }); await backend.connect(validConnectArgs); let caught: unknown; @@ -431,7 +431,7 @@ describe('SeaBackend — kernel error envelope decoding (DA-F1)', () => { it('falls back to original Error for a corrupted envelope, stripping the internal sentinel', async () => { const binding = bindingRejectingWith('not valid json'); - const backend = new SeaBackend({ nativeBinding: binding, context: makeFakeContext() }); + const backend = new KernelBackend({ nativeBinding: binding, context: makeFakeContext() }); await backend.connect(validConnectArgs); let caught: unknown; @@ -462,7 +462,7 @@ describe('SeaBackend — kernel error envelope decoding (DA-F1)', () => { '"sqlState":"08006","errorCode":"UPSTREAM_TIMEOUT","vendorCode":1234,' + '"httpStatus":503,"retryable":true,"queryId":"query-abc-123"}', ); - const backend = new SeaBackend({ nativeBinding: binding, context: makeFakeContext() }); + const backend = new KernelBackend({ nativeBinding: binding, context: makeFakeContext() }); await backend.connect(validConnectArgs); let caught: unknown; @@ -485,7 +485,7 @@ describe('SeaBackend — kernel error envelope decoding (DA-F1)', () => { it('keeps sqlState and kernelMetadata non-enumerable (matches Node `.code` pattern)', async () => { const binding = bindingRejectingWith('{"code":"NetworkError","message":"x","sqlState":"08000","httpStatus":502}'); - const backend = new SeaBackend({ nativeBinding: binding, context: makeFakeContext() }); + const backend = new KernelBackend({ nativeBinding: binding, context: makeFakeContext() }); await backend.connect(validConnectArgs); let caught: unknown; @@ -513,7 +513,7 @@ describe('SeaBackend — kernel error envelope decoding (DA-F1)', () => { const binding = bindingRejectingWith( '{"code":"Cancelled","message":"user-cancel","errorCode":"USER_REQUESTED_CANCEL"}', ); - const backend = new SeaBackend({ nativeBinding: binding, context: makeFakeContext() }); + const backend = new KernelBackend({ nativeBinding: binding, context: makeFakeContext() }); await backend.connect(validConnectArgs); let caught: unknown; @@ -543,7 +543,7 @@ describe('SeaBackend — kernel error envelope decoding (DA-F1)', () => { const binding = bindingRejectingWith( '{"code":"NetworkError","message":"x","errorCode":42,"vendorCode":"not-a-number","httpStatus":502,"retryable":"true","queryId":null}', ); - const backend = new SeaBackend({ nativeBinding: binding, context: makeFakeContext() }); + const backend = new KernelBackend({ nativeBinding: binding, context: makeFakeContext() }); await backend.connect(validConnectArgs); let caught: unknown; @@ -564,7 +564,7 @@ describe('SeaBackend — kernel error envelope decoding (DA-F1)', () => { // namespace because that's pure noise. The sqlState top-level // field is unaffected. const binding = bindingRejectingWith('{"code":"Internal","message":"x","sqlState":"08001"}'); - const backend = new SeaBackend({ nativeBinding: binding, context: makeFakeContext() }); + const backend = new KernelBackend({ nativeBinding: binding, context: makeFakeContext() }); await backend.connect(validConnectArgs); let caught: unknown; @@ -579,8 +579,8 @@ describe('SeaBackend — kernel error envelope decoding (DA-F1)', () => { expect(err.kernelMetadata).to.equal(undefined); }); - // NF-1: SeaSessionBackend.close() must wrap the napi call too. - it('SeaSessionBackend.close() decodes kernel-error envelopes from native.close()', async () => { + // NF-1: KernelSessionBackend.close() must wrap the napi call too. + it('KernelSessionBackend.close() decodes kernel-error envelopes from native.close()', async () => { const { binding } = makeFakeBinding(); // Make openSession return a fake Connection whose close() throws // a kernel-shaped envelope. @@ -594,7 +594,7 @@ describe('SeaBackend — kernel error envelope decoding (DA-F1)', () => { }; binding.openSession = (async () => failingClose as unknown) as typeof binding.openSession; - const backend = new SeaBackend({ nativeBinding: binding, context: makeFakeContext() }); + const backend = new KernelBackend({ nativeBinding: binding, context: makeFakeContext() }); await backend.connect(validConnectArgs); const session = await backend.openSession({}); diff --git a/tests/unit/sea/auth-m2m.test.ts b/tests/unit/kernel/auth-m2m.test.ts similarity index 82% rename from tests/unit/sea/auth-m2m.test.ts rename to tests/unit/kernel/auth-m2m.test.ts index 159afe1d..10249150 100644 --- a/tests/unit/sea/auth-m2m.test.ts +++ b/tests/unit/kernel/auth-m2m.test.ts @@ -13,15 +13,15 @@ // limitations under the License. import { expect } from 'chai'; -import SeaBackend from '../../../lib/sea/SeaBackend'; -import { buildSeaConnectionOptions } from '../../../lib/sea/SeaAuth'; +import KernelBackend from '../../../lib/kernel/KernelBackend'; +import { buildKernelConnectionOptions } from '../../../lib/kernel/KernelAuth'; import { ConnectionOptions } from '../../../lib/contracts/IDBSQLClient'; import AuthenticationError from '../../../lib/errors/AuthenticationError'; import HiveDriverError from '../../../lib/errors/HiveDriverError'; import { makeFakeBinding, makeFakeContext } from './_helpers/fakeBinding'; -describe('SeaAuth + SeaBackend — OAuth M2M auth flow', () => { - describe('buildSeaConnectionOptions', () => { +describe('KernelAuth + KernelBackend — OAuth M2M auth flow', () => { + describe('buildKernelConnectionOptions', () => { it('accepts databricks-oauth + oauthClientId + oauthClientSecret', () => { const opts: ConnectionOptions = { host: 'example.cloud.databricks.com', @@ -31,7 +31,7 @@ describe('SeaAuth + SeaBackend — OAuth M2M auth flow', () => { oauthClientSecret: 'dose-fake-secret', }; - const native = buildSeaConnectionOptions(opts); + const native = buildKernelConnectionOptions(opts); expect(native).to.deep.equal({ hostName: 'example.cloud.databricks.com', httpPath: '/sql/1.0/warehouses/abc', @@ -51,7 +51,7 @@ describe('SeaAuth + SeaBackend — OAuth M2M auth flow', () => { oauthClientSecret: 'dose-fake-secret', }; - const native = buildSeaConnectionOptions(opts); + const native = buildKernelConnectionOptions(opts); expect(native.httpPath).to.equal('/sql/1.0/warehouses/abc'); }); @@ -63,7 +63,7 @@ describe('SeaAuth + SeaBackend — OAuth M2M auth flow', () => { oauthClientSecret: 'dose-fake-secret', } as unknown as ConnectionOptions; - expect(() => buildSeaConnectionOptions(opts)).to.throw(AuthenticationError, /oauthClientId.*required/); + expect(() => buildKernelConnectionOptions(opts)).to.throw(AuthenticationError, /oauthClientId.*required/); }); it('rejects empty oauthClientId with AuthenticationError', () => { @@ -75,7 +75,7 @@ describe('SeaAuth + SeaBackend — OAuth M2M auth flow', () => { oauthClientSecret: 'dose-fake-secret', } as unknown as ConnectionOptions; - expect(() => buildSeaConnectionOptions(opts)).to.throw(AuthenticationError, /oauthClientId.*required/); + expect(() => buildKernelConnectionOptions(opts)).to.throw(AuthenticationError, /oauthClientId.*required/); }); it('rejects empty oauthClientSecret with AuthenticationError when oauthClientId is set (M2M intent)', () => { @@ -91,7 +91,7 @@ describe('SeaAuth + SeaBackend — OAuth M2M auth flow', () => { // is a typo/missing-env, not a request to fall back to U2M. // Surface the M2M "secret required" error so the user knows the // real problem instead of getting routed to a different flow. - expect(() => buildSeaConnectionOptions(opts)).to.throw( + expect(() => buildKernelConnectionOptions(opts)).to.throw( AuthenticationError, /oauthClientSecret.*non-empty.*OAuth M2M/, ); @@ -107,7 +107,10 @@ describe('SeaAuth + SeaBackend — OAuth M2M auth flow', () => { azureTenantId: 'tenant-uuid', }; - expect(() => buildSeaConnectionOptions(opts)).to.throw(HiveDriverError, /Azure-direct OAuth.*is not supported/); + expect(() => buildKernelConnectionOptions(opts)).to.throw( + HiveDriverError, + /Azure-direct OAuth.*is not supported/, + ); }); it('rejects useDatabricksOAuthInAzure with the same Entra-direct error', () => { @@ -120,7 +123,10 @@ describe('SeaAuth + SeaBackend — OAuth M2M auth flow', () => { useDatabricksOAuthInAzure: true, }; - expect(() => buildSeaConnectionOptions(opts)).to.throw(HiveDriverError, /Azure-direct OAuth.*is not supported/); + expect(() => buildKernelConnectionOptions(opts)).to.throw( + HiveDriverError, + /Azure-direct OAuth.*is not supported/, + ); }); it('rejects a `persistence` hook on M2M (no cache needed)', () => { @@ -136,17 +142,17 @@ describe('SeaAuth + SeaBackend — OAuth M2M auth flow', () => { }, }; - expect(() => buildSeaConnectionOptions(opts)).to.throw( + expect(() => buildKernelConnectionOptions(opts)).to.throw( HiveDriverError, /`persistence` is not supported on OAuth M2M/, ); }); }); - describe('SeaBackend.connect + openSession (M2M)', () => { + describe('KernelBackend.connect + openSession (M2M)', () => { it('round-trips M2M options through to the napi binding', async () => { const { binding, calls } = makeFakeBinding(); - const backend = new SeaBackend({ nativeBinding: binding, context: makeFakeContext() }); + const backend = new KernelBackend({ nativeBinding: binding, context: makeFakeContext() }); await backend.connect({ host: 'example.cloud.databricks.com', @@ -157,7 +163,7 @@ describe('SeaAuth + SeaBackend — OAuth M2M auth flow', () => { }); const session = await backend.openSession({}); - // Post-integration: SeaSessionBackend generates UUIDv4 ids; the + // Post-integration: KernelSessionBackend generates UUIDv4 ids; the // earlier auth-only counter-id scheme was superseded. expect(session.id).to.match(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i); @@ -178,7 +184,7 @@ describe('SeaAuth + SeaBackend — OAuth M2M auth flow', () => { it('rejects connect() for missing oauthClientId before touching the binding', async () => { const { binding, calls } = makeFakeBinding(); - const backend = new SeaBackend({ nativeBinding: binding, context: makeFakeContext() }); + const backend = new KernelBackend({ nativeBinding: binding, context: makeFakeContext() }); let caught: unknown; try { diff --git a/tests/unit/sea/auth-pat.test.ts b/tests/unit/kernel/auth-pat.test.ts similarity index 77% rename from tests/unit/sea/auth-pat.test.ts rename to tests/unit/kernel/auth-pat.test.ts index bd82eb87..4fd8b704 100644 --- a/tests/unit/sea/auth-pat.test.ts +++ b/tests/unit/kernel/auth-pat.test.ts @@ -13,13 +13,13 @@ // limitations under the License. import { expect } from 'chai'; -import { buildSeaConnectionOptions } from '../../../lib/sea/SeaAuth'; +import { buildKernelConnectionOptions } from '../../../lib/kernel/KernelAuth'; import { ConnectionOptions } from '../../../lib/contracts/IDBSQLClient'; import AuthenticationError from '../../../lib/errors/AuthenticationError'; import HiveDriverError from '../../../lib/errors/HiveDriverError'; -describe('SeaAuth — PAT auth options builder', () => { - describe('buildSeaConnectionOptions', () => { +describe('KernelAuth — PAT auth options builder', () => { + describe('buildKernelConnectionOptions', () => { it('accepts a bare access-token PAT (undefined authType)', () => { const opts: ConnectionOptions = { host: 'example.cloud.databricks.com', @@ -27,7 +27,7 @@ describe('SeaAuth — PAT auth options builder', () => { token: 'dapi-fake-pat', }; - const native = buildSeaConnectionOptions(opts); + const native = buildKernelConnectionOptions(opts); expect(native).to.deep.equal({ hostName: 'example.cloud.databricks.com', httpPath: '/sql/1.0/warehouses/abc', @@ -45,7 +45,7 @@ describe('SeaAuth — PAT auth options builder', () => { token: 'dapi-fake-pat', }; - const native = buildSeaConnectionOptions(opts); + const native = buildKernelConnectionOptions(opts); expect(native.authMode).to.equal('Pat'); if (native.authMode === 'Pat') { expect(native.token).to.equal('dapi-fake-pat'); @@ -59,7 +59,7 @@ describe('SeaAuth — PAT auth options builder', () => { token: 'dapi-fake-pat', }; - const native = buildSeaConnectionOptions(opts); + const native = buildKernelConnectionOptions(opts); expect(native.httpPath).to.equal('/sql/1.0/warehouses/abc'); }); @@ -71,7 +71,7 @@ describe('SeaAuth — PAT auth options builder', () => { // no token } as unknown as ConnectionOptions; - expect(() => buildSeaConnectionOptions(opts)).to.throw(AuthenticationError, /non-empty PAT/); + expect(() => buildKernelConnectionOptions(opts)).to.throw(AuthenticationError, /non-empty PAT/); }); it('throws AuthenticationError when token is an empty string', () => { @@ -81,7 +81,7 @@ describe('SeaAuth — PAT auth options builder', () => { token: '', }; - expect(() => buildSeaConnectionOptions(opts)).to.throw(AuthenticationError, /non-empty PAT/); + expect(() => buildKernelConnectionOptions(opts)).to.throw(AuthenticationError, /non-empty PAT/); }); it('accepts databricks-oauth without oauthClientSecret as the U2M happy path', () => { @@ -91,7 +91,7 @@ describe('SeaAuth — PAT auth options builder', () => { authType: 'databricks-oauth', }; - const native = buildSeaConnectionOptions(opts); + const native = buildKernelConnectionOptions(opts); expect(native.authMode).to.equal('OAuthU2m'); }); @@ -104,7 +104,10 @@ describe('SeaAuth — PAT auth options builder', () => { tokenProvider: { getToken: async () => 'tok' } as any, }; - expect(() => buildSeaConnectionOptions(opts)).to.throw(HiveDriverError, /unsupported auth mode 'token-provider'/); + expect(() => buildKernelConnectionOptions(opts)).to.throw( + HiveDriverError, + /unsupported auth mode 'token-provider'/, + ); }); it('rejects external-token, static-token, and custom auth modes', () => { @@ -116,16 +119,16 @@ describe('SeaAuth — PAT auth options builder', () => { path: '/p', authType, } as any; - expect(() => buildSeaConnectionOptions(opts)).to.throw(HiveDriverError, /unsupported auth mode/); + expect(() => buildKernelConnectionOptions(opts)).to.throw(HiveDriverError, /unsupported auth mode/); } }); }); - // Note: SeaBackend.connect/openSession round-trip + error-path coverage - // moved to tests/unit/sea/execution.test.ts during the sea-integration - // merge (the execution branch's SeaBackend constructor signature + // Note: KernelBackend.connect/openSession round-trip + error-path coverage + // moved to tests/unit/kernel/execution.test.ts during the kernel-integration + // merge (the execution branch's KernelBackend constructor signature // {context, nativeBinding} supersedes the auth-only (binding) shape). // OAuth-specific flow-dispatch tests live in auth-m2m.test.ts and // auth-u2m.test.ts; M2M end-to-end against a live workspace lives in - // tests/integration/sea/auth-m2m-e2e.test.ts. + // tests/integration/kernel/auth-m2m-e2e.test.ts. }); diff --git a/tests/unit/sea/auth-u2m.test.ts b/tests/unit/kernel/auth-u2m.test.ts similarity index 81% rename from tests/unit/sea/auth-u2m.test.ts rename to tests/unit/kernel/auth-u2m.test.ts index 828ca961..f4dff3ab 100644 --- a/tests/unit/sea/auth-u2m.test.ts +++ b/tests/unit/kernel/auth-u2m.test.ts @@ -13,15 +13,15 @@ // limitations under the License. import { expect } from 'chai'; -import SeaBackend from '../../../lib/sea/SeaBackend'; -import { buildSeaConnectionOptions } from '../../../lib/sea/SeaAuth'; +import KernelBackend from '../../../lib/kernel/KernelBackend'; +import { buildKernelConnectionOptions } from '../../../lib/kernel/KernelAuth'; import { ConnectionOptions } from '../../../lib/contracts/IDBSQLClient'; import AuthenticationError from '../../../lib/errors/AuthenticationError'; import HiveDriverError from '../../../lib/errors/HiveDriverError'; import { makeFakeBinding, makeFakeContext } from './_helpers/fakeBinding'; -describe('SeaAuth + SeaBackend — OAuth U2M auth flow', () => { - describe('buildSeaConnectionOptions', () => { +describe('KernelAuth + KernelBackend — OAuth U2M auth flow', () => { + describe('buildKernelConnectionOptions', () => { it('accepts databricks-oauth with no clientSecret as the U2M happy path (hardcoded port 8030)', () => { const opts: ConnectionOptions = { host: 'example.cloud.databricks.com', @@ -29,7 +29,7 @@ describe('SeaAuth + SeaBackend — OAuth U2M auth flow', () => { authType: 'databricks-oauth', }; - const native = buildSeaConnectionOptions(opts); + const native = buildKernelConnectionOptions(opts); expect(native).to.deep.equal({ hostName: 'example.cloud.databricks.com', httpPath: '/sql/1.0/warehouses/abc', @@ -58,7 +58,7 @@ describe('SeaAuth + SeaBackend — OAuth U2M auth flow', () => { oauthClientId: 'custom-client', }; - expect(() => buildSeaConnectionOptions(opts)).to.throw( + expect(() => buildKernelConnectionOptions(opts)).to.throw( AuthenticationError, /oauthClientSecret.*non-empty.*OAuth M2M/, ); @@ -71,7 +71,7 @@ describe('SeaAuth + SeaBackend — OAuth U2M auth flow', () => { authType: 'databricks-oauth', }; - const native = buildSeaConnectionOptions(opts); + const native = buildKernelConnectionOptions(opts); expect(native.httpPath).to.equal('/sql/1.0/warehouses/abc'); }); @@ -83,7 +83,10 @@ describe('SeaAuth + SeaBackend — OAuth U2M auth flow', () => { azureTenantId: 'tenant-uuid', }; - expect(() => buildSeaConnectionOptions(opts)).to.throw(HiveDriverError, /Azure-direct OAuth.*is not supported/); + expect(() => buildKernelConnectionOptions(opts)).to.throw( + HiveDriverError, + /Azure-direct OAuth.*is not supported/, + ); }); it('rejects useDatabricksOAuthInAzure on the U2M path', () => { @@ -94,7 +97,10 @@ describe('SeaAuth + SeaBackend — OAuth U2M auth flow', () => { useDatabricksOAuthInAzure: true, }; - expect(() => buildSeaConnectionOptions(opts)).to.throw(HiveDriverError, /Azure-direct OAuth.*is not supported/); + expect(() => buildKernelConnectionOptions(opts)).to.throw( + HiveDriverError, + /Azure-direct OAuth.*is not supported/, + ); }); it('rejects a `persistence` hook on U2M citing the AuthConfig::External kernel-plumbing gap', () => { @@ -108,14 +114,14 @@ describe('SeaAuth + SeaBackend — OAuth U2M auth flow', () => { }, }; - expect(() => buildSeaConnectionOptions(opts)).to.throw(HiveDriverError, /AuthConfig::External.*plumbing/); + expect(() => buildKernelConnectionOptions(opts)).to.throw(HiveDriverError, /AuthConfig::External.*plumbing/); }); }); - describe('SeaBackend.connect + openSession (U2M)', () => { + describe('KernelBackend.connect + openSession (U2M)', () => { it('round-trips U2M options through to the napi binding', async () => { const { binding, calls } = makeFakeBinding(); - const backend = new SeaBackend({ nativeBinding: binding, context: makeFakeContext() }); + const backend = new KernelBackend({ nativeBinding: binding, context: makeFakeContext() }); await backend.connect({ host: 'example.cloud.databricks.com', @@ -124,7 +130,7 @@ describe('SeaAuth + SeaBackend — OAuth U2M auth flow', () => { }); const session = await backend.openSession({}); - // Post-integration: SeaSessionBackend generates UUIDv4 ids; the + // Post-integration: KernelSessionBackend generates UUIDv4 ids; the // earlier auth-only counter-id scheme was superseded. expect(session.id).to.match(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i); diff --git a/tests/unit/sea/connectionOptions.test.ts b/tests/unit/kernel/connectionOptions.test.ts similarity index 62% rename from tests/unit/sea/connectionOptions.test.ts rename to tests/unit/kernel/connectionOptions.test.ts index 4869bd16..64388471 100644 --- a/tests/unit/sea/connectionOptions.test.ts +++ b/tests/unit/kernel/connectionOptions.test.ts @@ -13,42 +13,42 @@ // limitations under the License. import { expect } from 'chai'; -import { buildSeaConnectionOptions, buildSeaTlsOptions } from '../../../lib/sea/SeaAuth'; +import { buildKernelConnectionOptions, buildKernelTlsOptions } from '../../../lib/kernel/KernelAuth'; import { ConnectionOptions } from '../../../lib/contracts/IDBSQLClient'; import HiveDriverError from '../../../lib/errors/HiveDriverError'; const PAT = { host: 'h.databricks.com', path: '/sql/1.0/warehouses/abc', token: 'dapi-x' }; -// Cast helper: the SEA connection-tuning/TLS options live on the internal +// Cast helper: the kernel connection-tuning/TLS options live on the internal // surface, so tests build untyped option literals. const opts = (extra: Record) => ({ ...PAT, ...extra } as unknown as ConnectionOptions); -describe('SeaAuth connection options — intervalsAsString default', () => { +describe('KernelAuth connection options — intervalsAsString default', () => { it('always sets intervalsAsString:true (thrift-compatible interval rendering)', () => { - const native = buildSeaConnectionOptions(opts({})) as { intervalsAsString?: boolean }; + const native = buildKernelConnectionOptions(opts({})) as { intervalsAsString?: boolean }; expect(native.intervalsAsString).to.equal(true); }); it('does NOT force complexTypesAsJson (native Arrow nested types match Thrift)', () => { - const native = buildSeaConnectionOptions(opts({})) as { complexTypesAsJson?: boolean }; + const native = buildKernelConnectionOptions(opts({})) as { complexTypesAsJson?: boolean }; expect(native.complexTypesAsJson).to.equal(undefined); }); }); -describe('SeaAuth connection options — maxConnections', () => { +describe('KernelAuth connection options — maxConnections', () => { it('forwards a valid positive integer', () => { - const native = buildSeaConnectionOptions(opts({ maxConnections: 10 })) as { maxConnections?: number }; + const native = buildKernelConnectionOptions(opts({ maxConnections: 10 })) as { maxConnections?: number }; expect(native.maxConnections).to.equal(10); }); it('omits maxConnections when unset', () => { - const native = buildSeaConnectionOptions(opts({})) as { maxConnections?: number }; + const native = buildKernelConnectionOptions(opts({})) as { maxConnections?: number }; expect(native.maxConnections).to.equal(undefined); }); for (const bad of [0, -1, 1.5]) { it(`rejects non-positive-integer maxConnections (${bad})`, () => { - expect(() => buildSeaConnectionOptions(opts({ maxConnections: bad }))).to.throw( + expect(() => buildKernelConnectionOptions(opts({ maxConnections: bad }))).to.throw( HiveDriverError, /positive integer/, ); @@ -56,41 +56,44 @@ describe('SeaAuth connection options — maxConnections', () => { } it('rejects maxConnections beyond the u32 limit', () => { - expect(() => buildSeaConnectionOptions(opts({ maxConnections: 0x1_0000_0000 }))).to.throw( + expect(() => buildKernelConnectionOptions(opts({ maxConnections: 0x1_0000_0000 }))).to.throw( HiveDriverError, /u32 limit/, ); }); }); -describe('SeaAuth TLS options (buildSeaTlsOptions)', () => { +describe('KernelAuth TLS options (buildKernelTlsOptions)', () => { it('is empty by default (secure-by-default — kernel default verify-on)', () => { - expect(buildSeaTlsOptions(opts({}))).to.deep.equal({}); + expect(buildKernelTlsOptions(opts({}))).to.deep.equal({}); }); it('passes checkServerCertificate through verbatim (including false)', () => { - expect(buildSeaTlsOptions(opts({ checkServerCertificate: false }))).to.deep.equal({ + expect(buildKernelTlsOptions(opts({ checkServerCertificate: false }))).to.deep.equal({ checkServerCertificate: false, }); - expect(buildSeaTlsOptions(opts({ checkServerCertificate: true }))).to.deep.equal({ + expect(buildKernelTlsOptions(opts({ checkServerCertificate: true }))).to.deep.equal({ checkServerCertificate: true, }); }); it('normalises a PEM string to a Buffer', () => { const pem = '-----BEGIN CERTIFICATE-----\nMIIB...\n-----END CERTIFICATE-----\n'; - const tls = buildSeaTlsOptions(opts({ customCaCert: pem })); + const tls = buildKernelTlsOptions(opts({ customCaCert: pem })); expect(Buffer.isBuffer(tls.customCaCert)).to.equal(true); expect(tls.customCaCert?.toString('utf8')).to.equal(pem); }); it('passes a Buffer customCaCert through unchanged', () => { const buf = Buffer.from('-----BEGIN CERTIFICATE-----\nx\n-----END CERTIFICATE-----'); - expect(buildSeaTlsOptions(opts({ customCaCert: buf })).customCaCert).to.equal(buf); + expect(buildKernelTlsOptions(opts({ customCaCert: buf })).customCaCert).to.equal(buf); }); it('rejects a non-PEM string', () => { - expect(() => buildSeaTlsOptions(opts({ customCaCert: 'not-a-pem' }))).to.throw(HiveDriverError, /PEM certificate/); + expect(() => buildKernelTlsOptions(opts({ customCaCert: 'not-a-pem' }))).to.throw( + HiveDriverError, + /PEM certificate/, + ); }); it('rejects out-of-order / partial PEM markers (ordered match, not two substrings)', () => { @@ -100,20 +103,26 @@ describe('SeaAuth TLS options (buildSeaTlsOptions)', () => { const beginOnly = '-----BEGIN CERTIFICATE-----\nMIIB...\n'; const endOnly = 'MIIB...\n-----END CERTIFICATE-----'; for (const bad of [reversed, beginOnly, endOnly]) { - expect(() => buildSeaTlsOptions(opts({ customCaCert: bad })), bad).to.throw(HiveDriverError, /PEM certificate/); + expect(() => buildKernelTlsOptions(opts({ customCaCert: bad })), bad).to.throw( + HiveDriverError, + /PEM certificate/, + ); } }); it('rejects an empty Buffer', () => { - expect(() => buildSeaTlsOptions(opts({ customCaCert: Buffer.alloc(0) }))).to.throw(HiveDriverError, /empty/); + expect(() => buildKernelTlsOptions(opts({ customCaCert: Buffer.alloc(0) }))).to.throw(HiveDriverError, /empty/); }); it('rejects a non-string, non-Buffer customCaCert', () => { - expect(() => buildSeaTlsOptions(opts({ customCaCert: 123 }))).to.throw(HiveDriverError, /PEM string or a Buffer/); + expect(() => buildKernelTlsOptions(opts({ customCaCert: 123 }))).to.throw( + HiveDriverError, + /PEM string or a Buffer/, + ); }); it('folds TLS options into the full connection options', () => { - const native = buildSeaConnectionOptions(opts({ checkServerCertificate: false })) as { + const native = buildKernelConnectionOptions(opts({ checkServerCertificate: false })) as { checkServerCertificate?: boolean; }; expect(native.checkServerCertificate).to.equal(false); diff --git a/tests/unit/sea/error-mapping.test.ts b/tests/unit/kernel/error-mapping.test.ts similarity index 98% rename from tests/unit/sea/error-mapping.test.ts rename to tests/unit/kernel/error-mapping.test.ts index 8b5bdf70..d00c550a 100644 --- a/tests/unit/sea/error-mapping.test.ts +++ b/tests/unit/kernel/error-mapping.test.ts @@ -1,11 +1,11 @@ import { expect } from 'chai'; -import { mapKernelErrorToJsError, KernelErrorCode, KernelErrorShape } from '../../../lib/sea/SeaErrorMapping'; +import { mapKernelErrorToJsError, KernelErrorCode, KernelErrorShape } from '../../../lib/kernel/KernelErrorMapping'; import HiveDriverError from '../../../lib/errors/HiveDriverError'; import AuthenticationError from '../../../lib/errors/AuthenticationError'; import OperationStateError, { OperationStateErrorCode } from '../../../lib/errors/OperationStateError'; import ParameterError from '../../../lib/errors/ParameterError'; -describe('SeaErrorMapping.mapKernelErrorToJsError', () => { +describe('KernelErrorMapping.mapKernelErrorToJsError', () => { // The 13 kernel ErrorCode variants — kept in sync with src/kernel_error.rs:66-134. // Tabular driver: each row is (kernel code, expected class, optional extra assertion). type Case = { diff --git a/tests/unit/sea/execution.test.ts b/tests/unit/kernel/execution.test.ts similarity index 91% rename from tests/unit/sea/execution.test.ts rename to tests/unit/kernel/execution.test.ts index 0badd491..8afff033 100644 --- a/tests/unit/sea/execution.test.ts +++ b/tests/unit/kernel/execution.test.ts @@ -14,10 +14,10 @@ import { expect } from 'chai'; import sinon from 'sinon'; -import SeaBackend from '../../../lib/sea/SeaBackend'; -import SeaSessionBackend from '../../../lib/sea/SeaSessionBackend'; -import SeaOperationBackend from '../../../lib/sea/SeaOperationBackend'; -import { SeaNativeBinding, SeaConnection, SeaStatement } from '../../../lib/sea/SeaNativeLoader'; +import KernelBackend from '../../../lib/kernel/KernelBackend'; +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 IDBSQLLogger, { LogLevel } from '../../../lib/contracts/IDBSQLLogger'; import HiveDriverError from '../../../lib/errors/HiveDriverError'; @@ -29,10 +29,10 @@ import { OperationState } from '../../../lib/contracts/OperationStatus'; // ----------------------------------------------------------------------------- // Fakes — minimal stand-ins for the napi-rs generated surface and the // IClientContext side of the abstraction. Keeping them inline avoids -// pulling in test-only fixtures from outside the sea/ namespace. +// pulling in test-only fixtures from outside the kernel/ namespace. // ----------------------------------------------------------------------------- -class FakeNativeStatement implements SeaStatement { +class FakeNativeStatement implements KernelStatement { public closed = false; public cancelled = false; @@ -179,7 +179,7 @@ class FakeCancellableExecution { } } -class FakeNativeConnection implements SeaConnection { +class FakeNativeConnection implements KernelConnection { public closed = false; public lastSql?: string; @@ -209,10 +209,10 @@ class FakeNativeConnection implements SeaConnection { // sync `runAsync: false` query path — the DEFAULT). public lastCancellableExecution?: FakeCancellableExecution; - // The bare blocking executeStatement path: the SEA backend's sync default + // The bare blocking executeStatement path: the kernel backend's sync default // routes through executeStatementCancellable (below), but the binding still // exposes this for completeness. - public async executeStatement(sql: string, options?: unknown): Promise { + public async executeStatement(sql: string, options?: unknown): Promise { if (this.throwOnExecute) { throw this.throwOnExecute; } @@ -236,7 +236,7 @@ class FakeNativeConnection implements SeaConnection { // directResults (`runAsync: false`, the DEFAULT) query path: records sql + // options and returns either a terminal `Statement` (Completed arm) or — when // `directReturnsRunning` is set — a pending `AsyncStatement` (Running arm), - // the two arms `SeaSessionBackend.executeStatement` feature-detects via + // the two arms `KernelSessionBackend.executeStatement` feature-detects via // `awaitResult`. public directReturnsRunning = false; @@ -265,7 +265,7 @@ class FakeNativeConnection implements SeaConnection { return this.lastAsyncStatement; } - private recordMetadata(method: string, args: unknown[]): Promise { + private recordMetadata(method: string, args: unknown[]): Promise { this.metadataCalls.push([method, ...args]); return Promise.resolve(this.statementToReturn); } @@ -329,7 +329,7 @@ class FakeNativeConnection implements SeaConnection { } } -function makeBinding(connection: SeaConnection): SeaNativeBinding & { +function makeBinding(connection: KernelConnection): KernelNativeBinding & { openSessionStub: sinon.SinonStub; } { const openSessionStub = sinon.stub().resolves(connection); @@ -341,7 +341,7 @@ function makeBinding(connection: SeaConnection): SeaNativeBinding & { openSession: openSessionStub, Connection: function Connection() {}, Statement: function Statement() {}, - } as unknown as SeaNativeBinding; + } as unknown as KernelNativeBinding; return Object.assign(binding, { openSessionStub }); } @@ -356,13 +356,13 @@ function makeContext(logger?: IDBSQLLogger): IClientContext { getConfig: () => config, getLogger: () => log, getConnectionProvider: async () => { - throw new Error('not used by SEA backend'); + throw new Error('not used by kernel backend'); }, getClient: async () => { - throw new Error('not used by SEA backend'); + throw new Error('not used by kernel backend'); }, getDriver: async () => { - throw new Error('not used by SEA backend'); + throw new Error('not used by kernel backend'); }, }; } @@ -371,11 +371,11 @@ function makeContext(logger?: IDBSQLLogger): IClientContext { // Tests // ----------------------------------------------------------------------------- -describe('SeaBackend', () => { +describe('KernelBackend', () => { it('connect() captures the connection options and validates PAT auth', async () => { const connection = new FakeNativeConnection(); const binding = makeBinding(connection); - const backend = new SeaBackend({ context: makeContext(), nativeBinding: binding }); + const backend = new KernelBackend({ context: makeContext(), nativeBinding: binding }); await backend.connect({ host: 'example.databricks.com', @@ -387,8 +387,8 @@ describe('SeaBackend', () => { expect(binding.openSessionStub.called).to.equal(false); }); - // sea-auth-u2m: `databricks-oauth` with no id/secret is now the U2M happy - // path (M0 was PAT-only, but the OAuth M2M+U2M feature on sea-auth-u2m + // kernel-auth-u2m: `databricks-oauth` with no id/secret is now the U2M happy + // path (M0 was PAT-only, but the OAuth M2M+U2M feature on kernel-auth-u2m // accepts the full set of `databricks-oauth` variants). M2M/U2M flow- // dispatch coverage lives in auth-m2m.test.ts / auth-u2m.test.ts; // out-of-scope auth modes are now whatever neither PAT nor @@ -396,7 +396,7 @@ describe('SeaBackend', () => { it('connect() rejects unsupported auth modes (non-PAT, non-OAuth)', async () => { const connection = new FakeNativeConnection(); const binding = makeBinding(connection); - const backend = new SeaBackend({ context: makeContext(), nativeBinding: binding }); + const backend = new KernelBackend({ context: makeContext(), nativeBinding: binding }); let thrown: unknown; try { @@ -416,7 +416,7 @@ describe('SeaBackend', () => { it('connect() rejects missing token', async () => { const connection = new FakeNativeConnection(); const binding = makeBinding(connection); - const backend = new SeaBackend({ context: makeContext(), nativeBinding: binding }); + const backend = new KernelBackend({ context: makeContext(), nativeBinding: binding }); let thrown: unknown; try { @@ -429,8 +429,8 @@ describe('SeaBackend', () => { thrown = err; } expect(thrown).to.be.instanceOf(HiveDriverError); - // After sea-integration merge, missing-token validation goes through - // SeaAuth.buildSeaConnectionOptions which throws AuthenticationError + // After kernel-integration merge, missing-token validation goes through + // KernelAuth.buildKernelConnectionOptions which throws AuthenticationError // (extends HiveDriverError) with the "non-empty PAT" message. expect((thrown as Error).message).to.match(/non-empty PAT/); }); @@ -438,7 +438,7 @@ describe('SeaBackend', () => { it('openSession() throws if connect() was not called', async () => { const connection = new FakeNativeConnection(); const binding = makeBinding(connection); - const backend = new SeaBackend({ context: makeContext(), nativeBinding: binding }); + const backend = new KernelBackend({ context: makeContext(), nativeBinding: binding }); let thrown: unknown; try { @@ -453,7 +453,7 @@ describe('SeaBackend', () => { it('openSession() forwards hostName / httpPath / token to napi binding', async () => { const connection = new FakeNativeConnection(); const binding = makeBinding(connection); - const backend = new SeaBackend({ context: makeContext(), nativeBinding: binding }); + const backend = new KernelBackend({ context: makeContext(), nativeBinding: binding }); await backend.connect({ host: 'workspace.example', @@ -465,9 +465,9 @@ describe('SeaBackend', () => { expect(binding.openSessionStub.calledOnce).to.equal(true); const args = binding.openSessionStub.firstCall.args[0]; - // sea-auth-u2m introduced the discriminated SeaNativeConnectionOptions + // kernel-auth-u2m introduced the discriminated KernelNativeConnectionOptions // shape with a leading `authMode` tag — `'Pat'` for the PAT branch. - // `intervalsAsString: true` is always set so the SEA result shape is a + // `intervalsAsString: true` is always set so the kernel result shape is a // byte-compatible drop-in for the Thrift backend (interval-as-string). expect(args).to.deep.equal({ hostName: 'workspace.example', @@ -478,10 +478,10 @@ describe('SeaBackend', () => { }); }); - it('openSession() returns a SeaSessionBackend wrapping the napi Connection', async () => { + it('openSession() returns a KernelSessionBackend wrapping the napi Connection', async () => { const connection = new FakeNativeConnection(); const binding = makeBinding(connection); - const backend = new SeaBackend({ context: makeContext(), nativeBinding: binding }); + const backend = new KernelBackend({ context: makeContext(), nativeBinding: binding }); await backend.connect({ host: 'h', @@ -490,14 +490,14 @@ describe('SeaBackend', () => { } as ConnectionOptions); const sessionBackend = await backend.openSession({}); - expect(sessionBackend).to.be.instanceOf(SeaSessionBackend); + expect(sessionBackend).to.be.instanceOf(KernelSessionBackend); expect(sessionBackend.id).to.be.a('string').and.have.length.greaterThan(0); }); it('openSession() forwards initialCatalog / initialSchema / configuration to the napi openSession call (not per-statement)', async () => { const connection = new FakeNativeConnection(); const binding = makeBinding(connection); - const backend = new SeaBackend({ context: makeContext(), nativeBinding: binding }); + const backend = new KernelBackend({ context: makeContext(), nativeBinding: binding }); await backend.connect({ host: 'h', @@ -532,7 +532,7 @@ describe('SeaBackend', () => { it('close() clears connection state without throwing', async () => { const connection = new FakeNativeConnection(); const binding = makeBinding(connection); - const backend = new SeaBackend({ context: makeContext(), nativeBinding: binding }); + const backend = new KernelBackend({ context: makeContext(), nativeBinding: binding }); await backend.connect({ host: 'h', path: '/p', token: 't' } as ConnectionOptions); await backend.close(); @@ -546,9 +546,9 @@ describe('SeaBackend', () => { }); }); -describe('SeaSessionBackend', () => { - function makeSession(connection: SeaConnection) { - return new SeaSessionBackend({ connection, context: makeContext() }); +describe('KernelSessionBackend', () => { + function makeSession(connection: KernelConnection) { + return new KernelSessionBackend({ connection, context: makeContext() }); } it('executeStatement passes sql through verbatim', async () => { @@ -558,11 +558,11 @@ describe('SeaSessionBackend', () => { expect(connection.lastSql).to.equal('SELECT * FROM foo'); }); - it('executeStatement returns a SeaOperationBackend with an id', async () => { + it('executeStatement returns a KernelOperationBackend with an id', async () => { const connection = new FakeNativeConnection(); const session = makeSession(connection); const op = await session.executeStatement('SELECT 1', {}); - expect(op).to.be.instanceOf(SeaOperationBackend); + expect(op).to.be.instanceOf(KernelOperationBackend); expect(op.id).to.be.a('string').and.have.length.greaterThan(0); }); @@ -574,7 +574,7 @@ describe('SeaSessionBackend', () => { connection.directReturnsRunning = true; const session = makeSession(connection); const op = await session.executeStatement('SELECT slow', {}); - expect(op).to.be.instanceOf(SeaOperationBackend); + expect(op).to.be.instanceOf(KernelOperationBackend); // The Running arm was taken: an AsyncStatement was constructed + wired // (not the terminal `statement` arm). expect(connection.lastAsyncStatement, 'AsyncStatement (Running) arm should be taken').to.not.equal(undefined); @@ -649,17 +649,17 @@ describe('SeaSessionBackend', () => { expect((thrown as Error).message).to.equal('Driver does not support both ordinal and named parameters.'); }); - it('executeStatement (sync default) does NOT forward queryTimeout — no-op on SEA', async () => { + it('executeStatement (sync default) does NOT forward queryTimeout — no-op on kernel', async () => { const connection = new FakeNativeConnection(); const session = makeSession(connection); await session.executeStatement('SELECT 1', { queryTimeout: 30 }); - // queryTimeout is a no-op on SEA (SQL Warehouses use STATEMENT_TIMEOUT). It + // queryTimeout is a no-op on kernel (SQL Warehouses use STATEMENT_TIMEOUT). It // must NOT be mapped to the kernel's `wait_timeout` (the inline-hold window), // so nothing is forwarded onto the napi options. expect((connection.lastOptions as { queryTimeoutSecs?: number } | undefined)?.queryTimeoutSecs).to.equal(undefined); }); - it('executeStatement (runAsync: true) does NOT forward queryTimeout — no-op on SEA', async () => { + it('executeStatement (runAsync: true) does NOT forward queryTimeout — no-op on kernel', async () => { const connection = new FakeNativeConnection(); const session = makeSession(connection); await session.executeStatement('SELECT 1', { queryTimeout: 30, runAsync: true }); @@ -725,7 +725,7 @@ describe('SeaSessionBackend', () => { } }); - // Genuinely unsupported on SEA — rejected (rather than silently ignored) so + // Genuinely unsupported on kernel — rejected (rather than silently ignored) so // a caller/agent gets signal instead of a no-op. queryTags / queryTimeout / // rowLimit are NOT here — they are forwarded (asserted above). for (const { name, options, re } of [ @@ -748,14 +748,14 @@ describe('SeaSessionBackend', () => { } // Metadata calls forward to the kernel's metadata surface and wrap the - // returned napi `Statement` as a `SeaOperationBackend`. Each case asserts + // returned napi `Statement` as a `KernelOperationBackend`. Each case asserts // the request → napi-argument mapping (the only logic the driver owns). it('metadata calls forward to the napi binding with mapped arguments', async () => { const connection = new FakeNativeConnection(); const session = makeSession(connection); const op = await session.getCatalogs({}); - expect(op).to.be.instanceOf(SeaOperationBackend); + expect(op).to.be.instanceOf(KernelOperationBackend); await session.getSchemas({ catalogName: 'main', schemaName: 'def%' }); await session.getTables({ catalogName: 'main', schemaName: 'def', tableName: 't%', tableTypes: ['TABLE', 'VIEW'] }); @@ -857,9 +857,9 @@ describe('SeaSessionBackend', () => { }); }); -describe('SeaOperationBackend', () => { - function makeOperation(statement: SeaStatement = new FakeNativeStatement()) { - return new SeaOperationBackend({ statement, context: makeContext() }); +describe('KernelOperationBackend', () => { + function makeOperation(statement: KernelStatement = new FakeNativeStatement()) { + return new KernelOperationBackend({ statement, context: makeContext() }); } it('id is a stable string', () => { @@ -901,27 +901,30 @@ describe('SeaOperationBackend', () => { await op.waitUntilReady(); }); - // Note: after sea-integration merge, fetchChunk is no longer a stub — - // the sea-results SeaResultsProvider + ArrowResultConverter pipeline + // Note: after kernel-integration merge, fetchChunk is no longer a stub — + // the kernel-results KernelResultsProvider + ArrowResultConverter pipeline // implements the real fetch path. Full coverage lives in - // tests/unit/sea/SeaOperationBackend.test.ts and the parity-gate e2e - // at tests/e2e/sea/results-e2e.test.ts. + // tests/unit/kernel/KernelOperationBackend.test.ts and the parity-gate e2e + // at tests/e2e/kernel/results-e2e.test.ts. }); -describe('SeaOperationBackend — async (submitStatement) path', () => { +describe('KernelOperationBackend — async (submitStatement) path', () => { const makeAsyncOp = (asyncStatement: FakeAsyncStatement) => // eslint-disable-next-line @typescript-eslint/no-explicit-any - new SeaOperationBackend({ asyncStatement: asyncStatement as any, context: makeContext() }); + new KernelOperationBackend({ asyncStatement: asyncStatement as any, context: makeContext() }); it('rejects when neither asyncStatement nor statement is provided', () => { // eslint-disable-next-line @typescript-eslint/no-explicit-any - expect(() => new SeaOperationBackend({ context: makeContext() } as any)).to.throw(HiveDriverError, /exactly one/); + expect(() => new KernelOperationBackend({ context: makeContext() } as any)).to.throw( + HiveDriverError, + /exactly one/, + ); }); it('rejects when BOTH asyncStatement and statement are provided', () => { expect( () => - new SeaOperationBackend({ + new KernelOperationBackend({ // eslint-disable-next-line @typescript-eslint/no-explicit-any asyncStatement: new FakeAsyncStatement() as any, statement: new FakeNativeStatement(), @@ -1034,9 +1037,9 @@ describe('SeaOperationBackend — async (submitStatement) path', () => { }); }); -describe('SeaOperationBackend — sync (executeStatementCancellable) path', () => { +describe('KernelOperationBackend — sync (executeStatementCancellable) path', () => { const makeSyncOp = (cancellableExecution: FakeCancellableExecution) => - new SeaOperationBackend({ + new KernelOperationBackend({ // eslint-disable-next-line @typescript-eslint/no-explicit-any cancellableExecution: cancellableExecution as any, context: makeContext(), @@ -1045,7 +1048,7 @@ describe('SeaOperationBackend — sync (executeStatementCancellable) path', () = it('rejects when more than one handle kind is provided', () => { expect( () => - new SeaOperationBackend({ + new KernelOperationBackend({ // eslint-disable-next-line @typescript-eslint/no-explicit-any cancellableExecution: new FakeCancellableExecution() as any, statement: new FakeNativeStatement(), @@ -1060,7 +1063,7 @@ describe('SeaOperationBackend — sync (executeStatementCancellable) path', () = // summary). The server statement_id is surfaced via a debug log instead. const logs: Array<{ level: LogLevel; message: string }> = []; const logger: IDBSQLLogger = { log: (level, message) => logs.push({ level, message }) }; - const op = new SeaOperationBackend({ + const op = new KernelOperationBackend({ // eslint-disable-next-line @typescript-eslint/no-explicit-any cancellableExecution: new FakeCancellableExecution() as any, context: makeContext(logger), diff --git a/tests/unit/sea/inputValidation.test.ts b/tests/unit/kernel/inputValidation.test.ts similarity index 92% rename from tests/unit/sea/inputValidation.test.ts rename to tests/unit/kernel/inputValidation.test.ts index 6564b694..a5e11af1 100644 --- a/tests/unit/sea/inputValidation.test.ts +++ b/tests/unit/kernel/inputValidation.test.ts @@ -14,11 +14,11 @@ import { expect } from 'chai'; import Int64 from 'node-int64'; -import assertBindableValue from '../../../lib/sea/SeaInputValidation'; +import assertBindableValue from '../../../lib/kernel/KernelInputValidation'; import { DBSQLParameter, DBSQLParameterType } from '../../../lib/DBSQLParameter'; import ParameterError from '../../../lib/errors/ParameterError'; -describe('SeaInputValidation.assertBindableValue', () => { +describe('KernelInputValidation.assertBindableValue', () => { it('accepts scalars, Date, Int64, bigint, null, and DBSQLParameter', () => { expect(() => assertBindableValue(42, 'p')).to.not.throw(); expect(() => assertBindableValue('x', 'p')).to.not.throw(); diff --git a/tests/unit/sea/loader.test.ts b/tests/unit/kernel/loader.test.ts similarity index 83% rename from tests/unit/sea/loader.test.ts rename to tests/unit/kernel/loader.test.ts index 3eb642ac..a6105918 100644 --- a/tests/unit/sea/loader.test.ts +++ b/tests/unit/kernel/loader.test.ts @@ -13,9 +13,9 @@ // limitations under the License. import { expect } from 'chai'; -import { SeaNativeLoader, SeaNativeBinding } from '../../../lib/sea/SeaNativeLoader'; +import { KernelNativeLoader, KernelNativeBinding } from '../../../lib/kernel/KernelNativeLoader'; -// Pure-logic tests for SeaNativeLoader. These exercise the load-failure +// Pure-logic tests for KernelNativeLoader. These exercise the load-failure // hint branches, the Node-version gate, the shape check, and caching via // the injectable `load` and `nodeMajor` seams — so they run everywhere // regardless of whether a real `.node` is installed on the test machine @@ -25,7 +25,7 @@ import { SeaNativeLoader, SeaNativeBinding } from '../../../lib/sea/SeaNativeLoa // circuits them; the gate's own tests inject the version under test. const SUPPORTED_NODE_MAJOR = () => 18; -function stubBinding(overrides: Partial> = {}): SeaNativeBinding { +function stubBinding(overrides: Partial> = {}): KernelNativeBinding { return { version: () => '1.2.3', openSession: async () => ({}), @@ -35,7 +35,7 @@ function stubBinding(overrides: Partial> AsyncResultHandle: function AsyncResultHandle() {}, CancellableExecution: function CancellableExecution() {}, ...overrides, - } as unknown as SeaNativeBinding; + } as unknown as KernelNativeBinding; } function errWithCode(code: string, message: string): NodeJS.ErrnoException { @@ -56,11 +56,11 @@ function thrownMessage(fn: () => unknown): string { return expect.fail('expected the call to throw, but it did not') as never; } -describe('SeaNativeLoader', () => { +describe('KernelNativeLoader', () => { describe('successful load', () => { it('get() returns the binding from the injected loader', () => { const binding = stubBinding(); - const loader = new SeaNativeLoader(() => binding, SUPPORTED_NODE_MAJOR); + const loader = new KernelNativeLoader(() => binding, SUPPORTED_NODE_MAJOR); expect(loader.get()).to.equal(binding); expect(loader.tryGet()).to.equal(binding); }); @@ -68,7 +68,7 @@ describe('SeaNativeLoader', () => { it('caches the result — the load function runs at most once', () => { let calls = 0; const binding = stubBinding(); - const loader = new SeaNativeLoader(() => { + const loader = new KernelNativeLoader(() => { calls += 1; return binding; }, SUPPORTED_NODE_MAJOR); @@ -81,8 +81,8 @@ describe('SeaNativeLoader', () => { describe('load-failure hints', () => { it('MODULE_NOT_FOUND → "not installed" hint pointing at the README', () => { - const loader = new SeaNativeLoader(() => { - throw errWithCode('MODULE_NOT_FOUND', "Cannot find module '../../native/sea'"); + const loader = new KernelNativeLoader(() => { + throw errWithCode('MODULE_NOT_FOUND', "Cannot find module '../../native/kernel'"); }, SUPPORTED_NODE_MAJOR); expect(loader.tryGet()).to.equal(undefined); const msg = thrownMessage(() => loader.get()); @@ -91,7 +91,7 @@ describe('SeaNativeLoader', () => { }); it('ERR_DLOPEN_FAILED → includes the underlying dlerror string and remediation', () => { - const loader = new SeaNativeLoader(() => { + const loader = new KernelNativeLoader(() => { throw errWithCode('ERR_DLOPEN_FAILED', 'GLIBC_2.32 not found'); }, SUPPORTED_NODE_MAJOR); const msg = thrownMessage(() => loader.get()); @@ -101,14 +101,14 @@ describe('SeaNativeLoader', () => { }); it('a generic Error (no code) preserves its message', () => { - const loader = new SeaNativeLoader(() => { + const loader = new KernelNativeLoader(() => { throw new Error('totally unexpected'); }, SUPPORTED_NODE_MAJOR); expect(() => loader.get()).to.throw(/totally unexpected/); }); it('a non-Error throw is wrapped', () => { - const loader = new SeaNativeLoader(() => { + const loader = new KernelNativeLoader(() => { // eslint-disable-next-line no-throw-literal throw 'a string'; }, SUPPORTED_NODE_MAJOR); @@ -118,7 +118,7 @@ describe('SeaNativeLoader', () => { describe('shape check', () => { it('rejects a binding missing an expected export', () => { - const loader = new SeaNativeLoader(() => stubBinding({ openSession: undefined }), SUPPORTED_NODE_MAJOR); + const loader = new KernelNativeLoader(() => stubBinding({ openSession: undefined }), SUPPORTED_NODE_MAJOR); expect(loader.tryGet()).to.equal(undefined); const msg = thrownMessage(() => loader.get()); expect(msg).to.match(/missing expected export/); @@ -129,7 +129,7 @@ describe('SeaNativeLoader', () => { // An older cached .node would load but crash mid-query at e.g. // `submitStatement` / `executeStatementCancellable`; fail fast at load. for (const cls of ['AsyncStatement', 'AsyncResultHandle', 'CancellableExecution'] as const) { - const loader = new SeaNativeLoader(() => stubBinding({ [cls]: undefined }), SUPPORTED_NODE_MAJOR); + const loader = new KernelNativeLoader(() => stubBinding({ [cls]: undefined }), SUPPORTED_NODE_MAJOR); const msg = thrownMessage(() => loader.get()); expect(msg, cls).to.match(/missing expected export/); expect(msg, cls).to.contain(cls); @@ -140,7 +140,7 @@ describe('SeaNativeLoader', () => { describe('Node-version gate', () => { it('fails closed on a Node version below the floor', () => { let loadCalled = false; - const loader = new SeaNativeLoader( + const loader = new KernelNativeLoader( () => { loadCalled = true; return stubBinding(); @@ -152,7 +152,7 @@ describe('SeaNativeLoader', () => { }); it('fails closed when the Node version is unparseable (NaN)', () => { - const loader = new SeaNativeLoader( + const loader = new KernelNativeLoader( () => stubBinding(), () => NaN, ); diff --git a/tests/unit/sea/logging.test.ts b/tests/unit/kernel/logging.test.ts similarity index 92% rename from tests/unit/sea/logging.test.ts rename to tests/unit/kernel/logging.test.ts index 087cf737..76aed2bc 100644 --- a/tests/unit/sea/logging.test.ts +++ b/tests/unit/kernel/logging.test.ts @@ -18,9 +18,9 @@ import { logLevelToKernelLevel, formatKernelLine, installKernelLogBridge, -} from '../../../lib/sea/SeaLogging'; +} from '../../../lib/kernel/KernelLogging'; import { LogLevel } from '../../../lib/contracts/IDBSQLLogger'; -import { SeaNativeBinding, SeaNativeLogRecord } from '../../../lib/sea/SeaNativeLoader'; +import { KernelNativeBinding, KernelNativeLogRecord } from '../../../lib/kernel/KernelNativeLoader'; // Minimal recording logger. With `withLevelChange` (default) it exposes // `onLevelChange` + an `emitLevelChange` test helper to simulate a runtime @@ -57,14 +57,14 @@ function recordingLogger(opts: { withLevelChange?: boolean } = {}) { // A binding stub that captures the registered bridge callback + initial level, // and records each `setKernelLogLevel` retarget. -function captureBinding(overrides: Partial> = {}) { +function captureBinding(overrides: Partial> = {}) { const captured: { - cb?: (err: Error | null, records: Array) => void; + cb?: (err: Error | null, records: Array) => void; level?: string; levelChanges: string[]; } = { levelChanges: [] }; const binding = { - initKernelLogging: (cb: (err: Error | null, records: Array) => void, level: string) => { + initKernelLogging: (cb: (err: Error | null, records: Array) => void, level: string) => { captured.cb = cb; captured.level = level; }, @@ -72,11 +72,11 @@ function captureBinding(overrides: Partial { +describe('KernelLogging', () => { describe('kernelLevelToLogLevel', () => { it('maps the kernel levels onto driver LogLevels', () => { expect(kernelLevelToLogLevel('error')).to.equal(LogLevel.error); @@ -145,7 +145,7 @@ describe('SeaLogging', () => { }); it('is a no-op (no throw) on an older binding without the bridge export', () => { - const binding = {} as unknown as SeaNativeBinding; + const binding = {} as unknown as KernelNativeBinding; expect(() => installKernelLogBridge(binding, recordingLogger(), LogLevel.info)).to.not.throw(); }); }); @@ -191,7 +191,7 @@ describe('SeaLogging', () => { initKernelLogging: () => { captured.installed = true; }, - } as unknown as SeaNativeBinding; + } as unknown as KernelNativeBinding; const logger = recordingLogger(); const unsubscribe = installKernelLogBridge(binding, logger, LogLevel.info); expect(captured.installed).to.equal(true); diff --git a/tests/unit/sea/native-packaging.test.ts b/tests/unit/kernel/native-packaging.test.ts similarity index 85% rename from tests/unit/sea/native-packaging.test.ts rename to tests/unit/kernel/native-packaging.test.ts index f7218a91..f3f8508f 100644 --- a/tests/unit/sea/native-packaging.test.ts +++ b/tests/unit/kernel/native-packaging.test.ts @@ -21,12 +21,12 @@ import { join } from 'path'; // (e.g. `@databricks/sea-native-linux-x64-gnu-darwin-arm64`, and the doubled // `@databricks/sea-native-linux-x64-gnu-linux-x64-gnu`), so a published // install would never resolve a `.node`. The canonical name is -// `@databricks/databricks-sql-kernel-` (see native/sea/README.md and the -// SeaNativeLoader load-failure hint). -describe('SEA native binding — packaging (native/sea/index.js)', () => { +// `@databricks/databricks-sql-kernel-` (see native/kernel/README.md and the +// KernelNativeLoader load-failure hint). +describe('kernel native binding — packaging (native/kernel/index.js)', () => { // Resolved from the repo root (the cwd for `npm test`) so the test does not // depend on the module system's `__dirname`. - const indexJs = readFileSync(join(process.cwd(), 'native/sea/index.js'), 'utf8'); + const indexJs = readFileSync(join(process.cwd(), 'native/kernel/index.js'), 'utf8'); // Every `require('@databricks/...')` fallback in the generated router. const required = Array.from(indexJs.matchAll(/require\('(@databricks\/[^']+)'\)/g)).map((m) => m[1]); @@ -38,7 +38,7 @@ describe('SEA native binding — packaging (native/sea/index.js)', () => { it('every npm fallback uses the canonical @databricks/databricks-sql-kernel- name', () => { const triple = /^@databricks\/databricks-sql-kernel-[a-z0-9]+(-[a-z0-9]+)*$/; for (const name of required) { - expect(name, `unexpected SEA native package name: ${name}`).to.match(triple); + expect(name, `unexpected kernel native package name: ${name}`).to.match(triple); } }); diff --git a/tests/unit/sea/operation-lifecycle.test.ts b/tests/unit/kernel/operation-lifecycle.test.ts similarity index 81% rename from tests/unit/sea/operation-lifecycle.test.ts rename to tests/unit/kernel/operation-lifecycle.test.ts index 1148cd47..839a75d3 100644 --- a/tests/unit/sea/operation-lifecycle.test.ts +++ b/tests/unit/kernel/operation-lifecycle.test.ts @@ -13,13 +13,13 @@ // limitations under the License. /** - * Unit tests for the SEA operation lifecycle (`cancel`, `close`, - * `finished`) — both via the `SeaOperationLifecycle` helpers and - * via `SeaOperationBackend` which composes them. + * Unit tests for the kernel operation lifecycle (`cancel`, `close`, + * `finished`) — both via the `KernelOperationLifecycle` helpers and + * via `KernelOperationBackend` which composes them. * * We mock the napi binding's `Statement` handle so the test process * doesn't touch any native code; the helpers and the backend are - * structurally typed against `SeaStatementHandle` exactly so this + * structurally typed against `KernelStatementHandle` exactly so this * works. */ @@ -29,14 +29,14 @@ import { OperationStatus, OperationState } from '../../../lib/contracts/Operatio import IClientContext from '../../../lib/contracts/IClientContext'; import IDBSQLLogger, { LogLevel } from '../../../lib/contracts/IDBSQLLogger'; import { - SeaStatementHandle, + KernelStatementHandle, createLifecycleState, - seaCancel, - seaClose, - seaFinished, + kernelCancel, + kernelClose, + kernelFinished, failIfNotActive, -} from '../../../lib/sea/SeaOperationLifecycle'; -import SeaOperationBackend from '../../../lib/sea/SeaOperationBackend'; +} from '../../../lib/kernel/KernelOperationLifecycle'; +import KernelOperationBackend from '../../../lib/kernel/KernelOperationBackend'; import OperationStateError, { OperationStateErrorCode } from '../../../lib/errors/OperationStateError'; import HiveDriverError from '../../../lib/errors/HiveDriverError'; @@ -65,8 +65,8 @@ function makeContext(): IClientContext { } as unknown as IClientContext; } -function makeStatement(overrides: Partial = {}): { - handle: SeaStatementHandle; +function makeStatement(overrides: Partial = {}): { + handle: KernelStatementHandle; cancel: sinon.SinonStub; close: sinon.SinonStub; } { @@ -79,14 +79,14 @@ function makeStatement(overrides: Partial = {}): { }; } -describe('SeaOperationLifecycle (helpers)', () => { - describe('seaCancel', () => { +describe('KernelOperationLifecycle (helpers)', () => { + describe('kernelCancel', () => { it('calls statement.cancel() and resolves with a success Status', async () => { const ctx = makeContext(); const { handle, cancel } = makeStatement(); const state = createLifecycleState(); - const status = await seaCancel(state, handle, ctx, 'op-id-1'); + const status = await kernelCancel(state, handle, ctx, 'op-id-1'); expect(cancel.calledOnce).to.equal(true); expect(status.isSuccess).to.equal(true); @@ -98,8 +98,8 @@ describe('SeaOperationLifecycle (helpers)', () => { const { handle, cancel } = makeStatement(); const state = createLifecycleState(); - await seaCancel(state, handle, ctx, 'op-id-2'); - await seaCancel(state, handle, ctx, 'op-id-2'); + await kernelCancel(state, handle, ctx, 'op-id-2'); + await kernelCancel(state, handle, ctx, 'op-id-2'); expect(cancel.calledOnce).to.equal(true); }); @@ -110,7 +110,7 @@ describe('SeaOperationLifecycle (helpers)', () => { const state = createLifecycleState(); state.isClosed = true; - const status = await seaCancel(state, handle, ctx, 'op-id-3'); + const status = await kernelCancel(state, handle, ctx, 'op-id-3'); expect(cancel.called).to.equal(false); expect(status.isSuccess).to.equal(true); @@ -125,14 +125,14 @@ describe('SeaOperationLifecycle (helpers)', () => { const cancelPromise = new Promise((resolve) => { release = resolve; }); - const handle: SeaStatementHandle = { + const handle: KernelStatementHandle = { cancel: () => cancelPromise, close: async () => undefined, }; - const inflight = seaCancel(state, handle, ctx, 'op-id-4'); + const inflight = kernelCancel(state, handle, ctx, 'op-id-4'); - // Yield once so the synchronous prelude of seaCancel runs. + // Yield once so the synchronous prelude of kernelCancel runs. await Promise.resolve(); expect(state.isCancelled).to.equal(true); // Before the await resolves, failIfNotActive must already throw. @@ -146,7 +146,7 @@ describe('SeaOperationLifecycle (helpers)', () => { it('propagates binding errors via the kernel error mapping', async () => { const ctx = makeContext(); const state = createLifecycleState(); - const handle: SeaStatementHandle = { + const handle: KernelStatementHandle = { cancel: async () => { // Simulate the binding's JSON-envelope error format. const payload = JSON.stringify({ @@ -160,7 +160,7 @@ describe('SeaOperationLifecycle (helpers)', () => { let thrown: unknown; try { - await seaCancel(state, handle, ctx, 'op-err-1'); + await kernelCancel(state, handle, ctx, 'op-err-1'); } catch (err) { thrown = err; } @@ -178,19 +178,19 @@ describe('SeaOperationLifecycle (helpers)', () => { const { handle } = makeStatement(); const state = createLifecycleState(); - await seaCancel(state, handle, ctx, 'op-id-log'); + await kernelCancel(state, handle, ctx, 'op-id-log'); expect(logger.entries.some((e) => e.level === LogLevel.debug && e.message.includes('op-id-log'))).to.equal(true); }); }); - describe('seaClose', () => { + describe('kernelClose', () => { it('calls statement.close() and resolves with a success Status', async () => { const ctx = makeContext(); const { handle, close } = makeStatement(); const state = createLifecycleState(); - const status = await seaClose(state, handle, ctx, 'op-close-1'); + const status = await kernelClose(state, handle, ctx, 'op-close-1'); expect(close.calledOnce).to.equal(true); expect(status.isSuccess).to.equal(true); @@ -202,8 +202,8 @@ describe('SeaOperationLifecycle (helpers)', () => { const { handle, close } = makeStatement(); const state = createLifecycleState(); - await seaClose(state, handle, ctx, 'op-close-2'); - await seaClose(state, handle, ctx, 'op-close-2'); + await kernelClose(state, handle, ctx, 'op-close-2'); + await kernelClose(state, handle, ctx, 'op-close-2'); expect(close.calledOnce).to.equal(true); }); @@ -211,7 +211,7 @@ describe('SeaOperationLifecycle (helpers)', () => { it('propagates binding errors via the kernel error mapping', async () => { const ctx = makeContext(); const state = createLifecycleState(); - const handle: SeaStatementHandle = { + const handle: KernelStatementHandle = { cancel: async () => undefined, close: async () => { const payload = JSON.stringify({ @@ -224,7 +224,7 @@ describe('SeaOperationLifecycle (helpers)', () => { let thrown: unknown; try { - await seaClose(state, handle, ctx, 'op-err-close'); + await kernelClose(state, handle, ctx, 'op-err-close'); } catch (err) { thrown = err; } @@ -233,11 +233,11 @@ describe('SeaOperationLifecycle (helpers)', () => { }); }); - describe('seaFinished', () => { + describe('kernelFinished', () => { it('resolves immediately when no callback is provided (M0 no-op)', async () => { const state = createLifecycleState(); const start = Date.now(); - await seaFinished(state); + await kernelFinished(state); // Should be near-instantaneous — no 100ms poll. expect(Date.now() - start).to.be.lessThan(50); }); @@ -246,7 +246,7 @@ describe('SeaOperationLifecycle (helpers)', () => { const state = createLifecycleState(); const callback = sinon.stub(); - await seaFinished(state, { callback }); + await kernelFinished(state, { callback }); expect(callback.calledOnce).to.equal(true); const arg = callback.firstCall.args[0] as OperationStatus; @@ -262,7 +262,7 @@ describe('SeaOperationLifecycle (helpers)', () => { resolvedInsideCallback = true; }; - await seaFinished(state, { callback }); + await kernelFinished(state, { callback }); expect(resolvedInsideCallback).to.equal(true); }); @@ -272,7 +272,7 @@ describe('SeaOperationLifecycle (helpers)', () => { state.isCancelled = true; const callback = sinon.stub(); - await seaFinished(state, { callback }); + await kernelFinished(state, { callback }); expect(callback.called).to.equal(false); }); @@ -314,11 +314,11 @@ describe('SeaOperationLifecycle (helpers)', () => { }); }); -describe('SeaOperationBackend (lifecycle integration)', () => { +describe('KernelOperationBackend (lifecycle integration)', () => { it('cancel() forwards to statement.cancel()', async () => { const ctx = makeContext(); const { handle, cancel } = makeStatement(); - const op = new SeaOperationBackend({ statement: handle, context: ctx }); + const op = new KernelOperationBackend({ statement: handle, context: ctx }); const status = await op.cancel(); @@ -329,7 +329,7 @@ describe('SeaOperationBackend (lifecycle integration)', () => { it('close() forwards to statement.close()', async () => { const ctx = makeContext(); const { handle, close } = makeStatement(); - const op = new SeaOperationBackend({ statement: handle, context: ctx }); + const op = new KernelOperationBackend({ statement: handle, context: ctx }); const status = await op.close(); @@ -340,7 +340,7 @@ describe('SeaOperationBackend (lifecycle integration)', () => { it('finished() resolves immediately and fires the callback once', async () => { const ctx = makeContext(); const { handle } = makeStatement(); - const op = new SeaOperationBackend({ statement: handle, context: ctx }); + const op = new KernelOperationBackend({ statement: handle, context: ctx }); const responses: OperationStatus[] = []; const start = Date.now(); @@ -354,7 +354,7 @@ describe('SeaOperationBackend (lifecycle integration)', () => { it('fetchChunk after cancel throws the cancellation error', async () => { const ctx = makeContext(); const { handle } = makeStatement(); - const op = new SeaOperationBackend({ statement: handle, context: ctx }); + const op = new KernelOperationBackend({ statement: handle, context: ctx }); await op.cancel(); @@ -371,7 +371,7 @@ describe('SeaOperationBackend (lifecycle integration)', () => { it('cancel() is idempotent across the backend surface', async () => { const ctx = makeContext(); const { handle, cancel } = makeStatement(); - const op = new SeaOperationBackend({ statement: handle, context: ctx }); + const op = new KernelOperationBackend({ statement: handle, context: ctx }); await op.cancel(); await op.cancel(); @@ -383,7 +383,7 @@ describe('SeaOperationBackend (lifecycle integration)', () => { it('close() is idempotent across the backend surface', async () => { const ctx = makeContext(); const { handle, close } = makeStatement(); - const op = new SeaOperationBackend({ statement: handle, context: ctx }); + const op = new KernelOperationBackend({ statement: handle, context: ctx }); await op.close(); await op.close(); @@ -394,7 +394,7 @@ describe('SeaOperationBackend (lifecycle integration)', () => { it('status() reports FINISHED_STATE when active', async () => { const ctx = makeContext(); const { handle } = makeStatement(); - const op = new SeaOperationBackend({ statement: handle, context: ctx }); + const op = new KernelOperationBackend({ statement: handle, context: ctx }); const status = await op.status(false); expect(status.state).to.equal(OperationState.Succeeded); @@ -403,7 +403,7 @@ describe('SeaOperationBackend (lifecycle integration)', () => { it('status() reports CANCELED_STATE after cancel', async () => { const ctx = makeContext(); const { handle } = makeStatement(); - const op = new SeaOperationBackend({ statement: handle, context: ctx }); + const op = new KernelOperationBackend({ statement: handle, context: ctx }); await op.cancel(); const status = await op.status(false); @@ -413,7 +413,7 @@ describe('SeaOperationBackend (lifecycle integration)', () => { it('id getter is stable', () => { const ctx = makeContext(); const { handle } = makeStatement(); - const op = new SeaOperationBackend({ statement: handle, context: ctx, id: 'fixed-id' }); + const op = new KernelOperationBackend({ statement: handle, context: ctx, id: 'fixed-id' }); expect(op.id).to.equal('fixed-id'); expect(op.id).to.equal('fixed-id'); @@ -422,7 +422,7 @@ describe('SeaOperationBackend (lifecycle integration)', () => { it('id getter defaults to a uuid when none is supplied', () => { const ctx = makeContext(); const { handle } = makeStatement(); - const op = new SeaOperationBackend({ statement: handle, context: ctx }); + const op = new KernelOperationBackend({ statement: handle, context: ctx }); // RFC4122 v4 — 36 chars with hyphens at positions 8/13/18/23. expect(op.id).to.match(/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[0-9a-f]{4}-[0-9a-f]{12}$/); @@ -431,7 +431,7 @@ describe('SeaOperationBackend (lifecycle integration)', () => { it('hasResultSet is true by default (kernel always streams)', () => { const ctx = makeContext(); const { handle } = makeStatement(); - const op = new SeaOperationBackend({ statement: handle, context: ctx }); + const op = new KernelOperationBackend({ statement: handle, context: ctx }); expect(op.hasResultSet()).to.equal(true); }); diff --git a/tests/unit/sea/positionalParams.test.ts b/tests/unit/kernel/positionalParams.test.ts similarity index 82% rename from tests/unit/sea/positionalParams.test.ts rename to tests/unit/kernel/positionalParams.test.ts index d9902303..f6070147 100644 --- a/tests/unit/sea/positionalParams.test.ts +++ b/tests/unit/kernel/positionalParams.test.ts @@ -13,18 +13,18 @@ // limitations under the License. import { expect } from 'chai'; -import { buildSeaPositionalParams, buildSeaNamedParams } from '../../../lib/sea/SeaPositionalParams'; +import { buildKernelPositionalParams, buildKernelNamedParams } from '../../../lib/kernel/KernelPositionalParams'; import { DBSQLParameter, DBSQLParameterType } from '../../../lib/DBSQLParameter'; import ParameterError from '../../../lib/errors/ParameterError'; -describe('SeaPositionalParams.buildSeaPositionalParams', () => { +describe('KernelPositionalParams.buildKernelPositionalParams', () => { it('returns undefined for no params (keeps the no-options fast path)', () => { - expect(buildSeaPositionalParams(undefined)).to.equal(undefined); - expect(buildSeaPositionalParams([])).to.equal(undefined); + expect(buildKernelPositionalParams(undefined)).to.equal(undefined); + expect(buildKernelPositionalParams([])).to.equal(undefined); }); it('infers types from raw values, matching DBSQLParameter rules', () => { - expect(buildSeaPositionalParams([42, 'hello', true])).to.deep.equal([ + expect(buildKernelPositionalParams([42, 'hello', true])).to.deep.equal([ { sqlType: 'INTEGER', value: '42' }, { sqlType: 'STRING', value: 'hello' }, { sqlType: 'BOOLEAN', value: 'TRUE' }, @@ -32,7 +32,7 @@ describe('SeaPositionalParams.buildSeaPositionalParams', () => { }); const decimal = (value: string) => () => - buildSeaPositionalParams([new DBSQLParameter({ type: DBSQLParameterType.DECIMAL, value })]); + buildKernelPositionalParams([new DBSQLParameter({ type: DBSQLParameterType.DECIMAL, value })]); it('emits DECIMAL in the parenthesised DECIMAL(p,s) form the kernel codec requires', () => { expect(decimal('99.99')()).to.deep.equal([{ sqlType: 'DECIMAL(4,2)', value: '99.99' }]); @@ -65,7 +65,7 @@ describe('SeaPositionalParams.buildSeaPositionalParams', () => { it('collapses every INTERVAL subtype to the kernel codec\'s single "INTERVAL" type name', () => { expect( - buildSeaPositionalParams([ + buildKernelPositionalParams([ new DBSQLParameter({ type: DBSQLParameterType.INTERVALMONTH, value: '13' }), new DBSQLParameter({ type: DBSQLParameterType.INTERVALDAY, value: '1 02:03:04' }), ]), @@ -76,12 +76,12 @@ describe('SeaPositionalParams.buildSeaPositionalParams', () => { }); it('maps NULL to a value-less VOID input', () => { - expect(buildSeaPositionalParams([null])).to.deep.equal([{ sqlType: 'VOID' }]); + expect(buildKernelPositionalParams([null])).to.deep.equal([{ sqlType: 'VOID' }]); }); it('honours explicit DATE / TIMESTAMP types', () => { expect( - buildSeaPositionalParams([ + buildKernelPositionalParams([ new DBSQLParameter({ type: DBSQLParameterType.DATE, value: '2024-01-15' }), new DBSQLParameter({ type: DBSQLParameterType.TIMESTAMP, value: '2024-01-15 10:30:00' }), ]), @@ -93,7 +93,7 @@ describe('SeaPositionalParams.buildSeaPositionalParams', () => { it('binds TIMESTAMP_NTZ natively and TIMESTAMP_LTZ as TIMESTAMP (Spark has no distinct LTZ type)', () => { expect( - buildSeaPositionalParams([ + buildKernelPositionalParams([ new DBSQLParameter({ type: DBSQLParameterType.TIMESTAMP_NTZ, value: '2024-01-15 10:30:00' }), new DBSQLParameter({ type: DBSQLParameterType.TIMESTAMP_LTZ, value: '2024-01-15 10:30:00' }), ]), @@ -104,15 +104,15 @@ describe('SeaPositionalParams.buildSeaPositionalParams', () => { }); }); -describe('SeaPositionalParams.buildSeaNamedParams', () => { +describe('KernelPositionalParams.buildKernelNamedParams', () => { it('returns undefined for no named params', () => { - expect(buildSeaNamedParams(undefined)).to.equal(undefined); - expect(buildSeaNamedParams({})).to.equal(undefined); + expect(buildKernelNamedParams(undefined)).to.equal(undefined); + expect(buildKernelNamedParams({})).to.equal(undefined); }); it('emits {name, sqlType, value} triples, reusing the same type mapping', () => { expect( - buildSeaNamedParams({ + buildKernelNamedParams({ n: 42, s: 'hello', d: new DBSQLParameter({ type: DBSQLParameterType.DECIMAL, value: '99.99' }), @@ -125,6 +125,6 @@ describe('SeaPositionalParams.buildSeaNamedParams', () => { }); it('maps a named NULL to a value-less VOID input (with the name)', () => { - expect(buildSeaNamedParams({ x: null })).to.deep.equal([{ name: 'x', sqlType: 'VOID' }]); + expect(buildKernelNamedParams({ x: null })).to.deep.equal([{ name: 'x', sqlType: 'VOID' }]); }); }); diff --git a/tests/unit/sea/serverInfo.test.ts b/tests/unit/kernel/serverInfo.test.ts similarity index 50% rename from tests/unit/sea/serverInfo.test.ts rename to tests/unit/kernel/serverInfo.test.ts index 4a027864..a8568de6 100644 --- a/tests/unit/sea/serverInfo.test.ts +++ b/tests/unit/kernel/serverInfo.test.ts @@ -13,35 +13,40 @@ // limitations under the License. import { expect } from 'chai'; -import { seaServerInfoValue, SEA_DBMS_NAME, SEA_SERVER_NAME, SEA_DBMS_VERSION } from '../../../lib/sea/SeaServerInfo'; +import { + kernelServerInfoValue, + KERNEL_DBMS_NAME, + KERNEL_SERVER_NAME, + KERNEL_DBMS_VERSION, +} from '../../../lib/kernel/KernelServerInfo'; import { TGetInfoType } from '../../../thrift/TCLIService_types'; -describe('SeaServerInfo.seaServerInfoValue', () => { +describe('KernelServerInfo.kernelServerInfoValue', () => { it('CLI_DBMS_NAME matches Thrift exactly ("Spark SQL")', () => { - expect(seaServerInfoValue(TGetInfoType.CLI_DBMS_NAME)?.stringValue).to.equal('Spark SQL'); - expect(SEA_DBMS_NAME).to.equal('Spark SQL'); + expect(kernelServerInfoValue(TGetInfoType.CLI_DBMS_NAME)?.stringValue).to.equal('Spark SQL'); + expect(KERNEL_DBMS_NAME).to.equal('Spark SQL'); }); it('CLI_DBMS_VER matches the Thrift server version constant', () => { - expect(seaServerInfoValue(TGetInfoType.CLI_DBMS_VER)?.stringValue).to.equal('3.1.1'); - expect(SEA_DBMS_VERSION).to.equal('3.1.1'); + expect(kernelServerInfoValue(TGetInfoType.CLI_DBMS_VER)?.stringValue).to.equal('3.1.1'); + expect(KERNEL_DBMS_VERSION).to.equal('3.1.1'); }); it('CLI_SERVER_NAME matches Thrift exactly ("Spark SQL")', () => { - const v = seaServerInfoValue(TGetInfoType.CLI_SERVER_NAME)?.stringValue; - expect(v).to.equal(SEA_SERVER_NAME); + const v = kernelServerInfoValue(TGetInfoType.CLI_SERVER_NAME)?.stringValue; + expect(v).to.equal(KERNEL_SERVER_NAME); expect(v).to.equal('Spark SQL'); }); it('all three answered info types are byte-identical to Thrift', () => { - expect(seaServerInfoValue(TGetInfoType.CLI_SERVER_NAME)?.stringValue).to.equal('Spark SQL'); - expect(seaServerInfoValue(TGetInfoType.CLI_DBMS_NAME)?.stringValue).to.equal('Spark SQL'); - expect(seaServerInfoValue(TGetInfoType.CLI_DBMS_VER)?.stringValue).to.equal('3.1.1'); + expect(kernelServerInfoValue(TGetInfoType.CLI_SERVER_NAME)?.stringValue).to.equal('Spark SQL'); + expect(kernelServerInfoValue(TGetInfoType.CLI_DBMS_NAME)?.stringValue).to.equal('Spark SQL'); + expect(kernelServerInfoValue(TGetInfoType.CLI_DBMS_VER)?.stringValue).to.equal('3.1.1'); }); it('returns undefined for info types the Thrift server rejects (e.g. CLI_MAX_DRIVER_CONNECTIONS)', () => { - expect(seaServerInfoValue(TGetInfoType.CLI_MAX_DRIVER_CONNECTIONS)).to.equal(undefined); - expect(seaServerInfoValue(TGetInfoType.CLI_USER_NAME)).to.equal(undefined); - expect(seaServerInfoValue(99999)).to.equal(undefined); + expect(kernelServerInfoValue(TGetInfoType.CLI_MAX_DRIVER_CONNECTIONS)).to.equal(undefined); + expect(kernelServerInfoValue(TGetInfoType.CLI_USER_NAME)).to.equal(undefined); + expect(kernelServerInfoValue(99999)).to.equal(undefined); }); }); diff --git a/tests/unit/sea/version.test.ts b/tests/unit/kernel/version.test.ts similarity index 83% rename from tests/unit/sea/version.test.ts rename to tests/unit/kernel/version.test.ts index 34603480..0efca43a 100644 --- a/tests/unit/sea/version.test.ts +++ b/tests/unit/kernel/version.test.ts @@ -13,12 +13,12 @@ // limitations under the License. import { expect } from 'chai'; -import { tryGetSeaNative } from '../../../lib/sea/SeaNativeLoader'; +import { tryGetKernelNative } from '../../../lib/kernel/KernelNativeLoader'; // Fail loudly only when the binding is actually expected to be present — // i.e. a CI step has provisioned it (a published `@databricks/databricks-sql-kernel-*` // optional dep installed, or `npm run build:native` was run) and opts in via -// `SEA_NATIVE_EXPECTED=1`. A missing binding there is a real packaging / build +// `KERNEL_NATIVE_EXPECTED=1`. A missing binding there is a real packaging / build // regression that a silent skip would mask. // // Until those binding packages are published, the standard CI cannot install @@ -26,17 +26,17 @@ import { tryGetSeaNative } from '../../../lib/sea/SeaNativeLoader'; // legitimately absent — default to a skip rather than a spurious hard failure. // (`npm ci` already skips the unpublished optional dep.) function bindingIsExpected(): boolean { - return process.env.SEA_NATIVE_EXPECTED === '1'; + return process.env.KERNEL_NATIVE_EXPECTED === '1'; } -describe('SEA native binding — smoke test', function smoke() { - const binding = tryGetSeaNative(); +describe('kernel native binding — smoke test', function smoke() { + const binding = tryGetKernelNative(); if (binding === undefined) { if (bindingIsExpected()) { it('fails loudly: the binding must load on the linux-x64 CI runner', () => { expect.fail( - 'SEA native binding failed to load on a linux-x64 CI runner where ' + + 'kernel native binding failed to load on a linux-x64 CI runner where ' + '@databricks/databricks-sql-kernel-linux-x64-gnu is expected. Run `npm run build:native` or check packaging.', ); }); @@ -45,7 +45,7 @@ describe('SEA native binding — smoke test', function smoke() { // Optional dependency absent on this platform — skip rather than fail. // eslint-disable-next-line no-invalid-this this.pending = true; - it.skip('SEA native binding not available on this platform'); + it.skip('kernel native binding not available on this platform'); return; }