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 11f05c1f..53114d88 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,7 +182,7 @@ jobs: checks: write id-token: write env: - # SEA e2e tests gate on the DATABRICKS_PECOTESTING_* vars; map the + # kernel e2e tests gate on the DATABRICKS_PECOTESTING_* vars; map the # warehouse secrets onto them so the suite actually runs (it skips # when they are absent). DATABRICKS_PECOTESTING_SERVER_HOSTNAME: ${{ secrets.DATABRICKS_HOST }} @@ -273,7 +273,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: @@ -281,28 +281,28 @@ 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 # Invoke mocha directly rather than via `npm run e2e -- `: routing a # glob through the npm-script's inner shell mangles `**` and silently # resolves to ZERO files (a false pass). mocha expands the quoted glob - # itself, reliably matching every tests/e2e/sea file. - run: NODE_OPTIONS="--max-old-space-size=4096" npx mocha --config tests/e2e/.mocharc.js "tests/e2e/sea/**/*.test.ts" + # itself, reliably matching every tests/e2e/kernel file. + run: NODE_OPTIONS="--max-old-space-size=4096" npx mocha --config tests/e2e/.mocharc.js "tests/e2e/kernel/**/*.test.ts" - name: Post Kernel E2E check (success) if: success() @@ -320,7 +320,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.' } }); @@ -345,7 +345,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: @@ -371,7 +371,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..7b2f225c 100644 --- a/.npmignore +++ b/.npmignore @@ -7,8 +7,8 @@ # 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..624b5446 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 + // 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_sea")` // 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/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/IDBSQLSession.ts b/lib/contracts/IDBSQLSession.ts index fd0dda16..79a3e85a 100644 --- a/lib/contracts/IDBSQLSession.ts +++ b/lib/contracts/IDBSQLSession.ts @@ -28,13 +28,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..174c95e1 100644 --- a/lib/contracts/InternalConnectionOptions.ts +++ b/lib/contracts/InternalConnectionOptions.ts @@ -7,7 +7,7 @@ * 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 95071895..38e7ce4e 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. @@ -198,7 +198,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 INTERVAL_DAY_TIME + // `KernelArrowIpcDurationFix.ts`). Surface them as INTERVAL_DAY_TIME // so the converter formats them back into the thrift string form. if (arrowType.bitWidth === 64 && field.metadata.has(DURATION_UNIT_METADATA_KEY)) { return TTypeId.INTERVAL_DAY_TIME_TYPE; @@ -246,7 +246,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 83% rename from lib/sea/SeaAuth.ts rename to lib/kernel/KernelAuth.ts index 3dd62cef..6cd521ba 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. @@ -103,9 +103,9 @@ export interface SeaSessionDefaults { * `buildSeaConnectionOptions` 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 buildSeaTlsOptions(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; @@ -203,7 +203,7 @@ export function buildSeaTlsOptions(options: ConnectionOptions): SeaTlsOptions { !customCaCert.includes('-----END CERTIFICATE-----') ) { 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 ' + "(missing the '-----BEGIN CERTIFICATE-----' / '-----END CERTIFICATE-----' markers). " + 'Pass PEM text or a Buffer of PEM bytes.', ); @@ -211,11 +211,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.'); } } @@ -241,18 +241,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 @@ -275,7 +275,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 buildSeaConnectionOptions(options: ConnectionOptions): KernelNativeConnectionOptions { const { authType } = options as { authType?: string }; const base: { @@ -283,12 +283,12 @@ 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. @@ -298,17 +298,19 @@ export function buildSeaConnectionOptions(options: ConnectionOptions): SeaNative ...buildSeaTlsOptions(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.', ); } @@ -327,12 +329,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.", ); @@ -343,14 +345,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.', ); @@ -358,12 +360,12 @@ 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 + // where thrift would run U2M, and kernel U2M has no custom-client-id support // (see buildSeaConnectionOptions 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"`) @@ -385,14 +387,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; ' + @@ -410,17 +412,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).', ); } @@ -433,8 +435,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 72% rename from lib/sea/SeaBackend.ts rename to lib/kernel/KernelBackend.ts index 1043da8d..77c437cf 100644 --- a/lib/sea/SeaBackend.ts +++ b/lib/kernel/KernelBackend.ts @@ -17,16 +17,16 @@ import ISessionBackend from '../contracts/ISessionBackend'; import IClientContext from '../contracts/IClientContext'; import { ConnectionOptions, OpenSessionRequest } from '../contracts/IDBSQLClient'; import HiveDriverError from '../errors/HiveDriverError'; -import { getSeaNative, SeaNativeBinding, SeaConnection } from './SeaNativeLoader'; -import { decodeNapiKernelError } from './SeaErrorMapping'; -import { buildSeaConnectionOptions, SeaNativeConnectionOptions } from './SeaAuth'; -import SeaSessionBackend from './SeaSessionBackend'; +import { getSeaNative, KernelNativeBinding, KernelConnection } from './KernelNativeLoader'; +import { decodeNapiKernelError } from './KernelErrorMapping'; +import { buildSeaConnectionOptions, KernelNativeConnectionOptions } from './KernelAuth'; +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. */ @@ -36,11 +36,11 @@ export interface SeaBackendOptions { * default `getSeaNative()` 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 @@ -49,26 +49,26 @@ export interface SeaBackendOptions { * use. The actual session open happens inside `openSession()`. * * **Auth validation:** delegates to `buildSeaConnectionOptions` from - * `SeaAuth`, which mirrors the existing DBSQLClient validation pattern + * `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; - constructor(options: SeaBackendOptions) { + constructor(options: KernelBackendOptions) { this.context = options.context; this.binding = options.nativeBinding ?? getSeaNative(); } @@ -82,7 +82,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 @@ -94,7 +94,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; } @@ -105,22 +105,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, @@ -128,7 +128,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 b17d594a..7429a3d7 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 @@ -222,7 +222,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/SeaNativeLoader.ts b/lib/kernel/KernelNativeLoader.ts similarity index 74% rename from lib/sea/SeaNativeLoader.ts rename to lib/kernel/KernelNativeLoader.ts index 75004e51..db368d81 100644 --- a/lib/sea/SeaNativeLoader.ts +++ b/lib/kernel/KernelNativeLoader.ts @@ -19,12 +19,12 @@ * `.node` artifact ships via per-platform optional dependencies * (`@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 + * kernel construct a {@link KernelNativeLoader} (or use the process-global * {@link getSeaNative}) 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 { @@ -38,18 +38,18 @@ import type { NamedTypedValueInput as NativeNamedTypedValueInput, AsyncStatement as NativeAsyncStatement, AsyncResultHandle as NativeAsyncResultHandle, -} 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` @@ -57,17 +57,17 @@ export type SeaStatement = NativeStatement; // `rowLimit`, `queryTimeoutSecs`, `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; /** * The full native binding surface, derived from the generated module @@ -75,7 +75,7 @@ export type SeaNativeAsyncResultHandle = NativeAsyncResultHandle; * 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; @@ -96,31 +96,31 @@ function loadFailureHint(err: NodeJS.ErrnoException): string { // 404. Point at the README's supported-triple list instead. const installHint = 'Install the matching @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; } /** @@ -128,7 +128,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'); @@ -136,21 +136,21 @@ function assertBindingShape(binding: SeaNativeBinding): void { if (typeof binding.Statement !== 'function') missing.push('Statement'); 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}. */ -export class SeaNativeLoader { - private cached: SeaNativeBinding | null | undefined; +export class KernelNativeLoader { + private cached: KernelNativeBinding | null | undefined; private cachedError: Error | undefined; @@ -162,17 +162,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; @@ -189,7 +189,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; } @@ -199,12 +199,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; } @@ -212,9 +212,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; } @@ -223,13 +223,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 getSeaNative(): KernelNativeBinding { return defaultLoader.get(); } @@ -237,6 +237,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 tryGetSeaNative(): KernelNativeBinding | undefined { return defaultLoader.tryGet(); } diff --git a/lib/sea/SeaOperationBackend.ts b/lib/kernel/KernelOperationBackend.ts similarity index 86% rename from lib/sea/SeaOperationBackend.ts rename to lib/kernel/KernelOperationBackend.ts index 0831d9ea..e229144a 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` → + * `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()` / * `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,19 +48,19 @@ 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 { SeaStatement, SeaNativeAsyncStatement, SeaNativeAsyncResultHandle } from './SeaNativeLoader'; +import KernelResultsProvider from './KernelResultsProvider'; +import { arrowSchemaToThriftSchema, decodeIpcSchema, patchIpcBytes } from './KernelArrowIpc'; +import { decodeNapiKernelError } from './KernelErrorMapping'; +import { KernelStatement, KernelNativeAsyncStatement, KernelNativeAsyncResultHandle } from './KernelNativeLoader'; import { - SeaStatementHandle, - SeaOperationLifecycleState, + KernelStatementHandle, + KernelOperationLifecycleState, createLifecycleState, seaCancel, seaClose, seaFinished, failIfNotActive, -} from './SeaOperationLifecycle'; +} from './KernelOperationLifecycle'; /** * Structural union of the lifecycle surface (cancel/close) and the @@ -69,16 +69,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; @@ -107,15 +107,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; context: IClientContext; /** * Optional override for `id`. Defaults to the napi statement-id when the @@ -133,27 +133,27 @@ export interface SeaOperationBackendOptions { queryTimeoutSecs?: number; } -export default class SeaOperationBackend implements IOperationBackend { +export default class KernelOperationBackend implements IOperationBackend { // Query path: pending async statement we poll to terminal. Undefined on the // metadata path. - private readonly asyncStatement?: SeaNativeAsyncStatement; + private readonly asyncStatement?: KernelNativeAsyncStatement; // Metadata path: terminal statement. Undefined on the query path. - private readonly blockingStatement?: SeaOperationStatement; + private readonly blockingStatement?: KernelOperationStatement; // The cancel/close surface — whichever handle backs this operation. Both // `AsyncStatement` and `Statement` expose `cancel()` / `close()`. - 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; @@ -162,19 +162,21 @@ 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; // Client-side query-timeout deadline in ms (the public `queryTimeout`), // undefined when unset. Enforced in the async poll loop. private readonly queryTimeoutMs?: number; - constructor({ asyncStatement, statement, context, id, queryTimeoutSecs }: SeaOperationBackendOptions) { + constructor({ asyncStatement, statement, context, id, queryTimeoutSecs }: KernelOperationBackendOptions) { if ((asyncStatement === undefined) === (statement === undefined)) { - throw new HiveDriverError('SeaOperationBackend: exactly one of `asyncStatement` or `statement` must be provided'); + throw new HiveDriverError( + 'KernelOperationBackend: exactly one of `asyncStatement` or `statement` must be provided', + ); } this.asyncStatement = asyncStatement; this.blockingStatement = statement; - this.lifecycleHandle = (asyncStatement ?? statement) as SeaStatementHandle; + this.lifecycleHandle = (asyncStatement ?? statement) as KernelStatementHandle; this.context = context; this._id = id ?? asyncStatement?.statementId ?? statement?.statementId ?? uuidv4(); this.queryTimeoutMs = queryTimeoutSecs !== undefined && queryTimeoutSecs > 0 ? queryTimeoutSecs * 1000 : undefined; @@ -185,8 +187,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. @@ -239,7 +241,7 @@ export default class SeaOperationBackend implements IOperationBackend { .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}`, ); }); @@ -267,7 +269,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. @@ -278,7 +280,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, @@ -431,25 +433,27 @@ 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.`); } /** * 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 { 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; @@ -461,9 +465,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 86% rename from lib/sea/SeaOperationLifecycle.ts rename to lib/kernel/KernelOperationLifecycle.ts index e640b2ab..4a209f3a 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`): @@ -120,8 +120,8 @@ function rethrowKernelError(err: unknown): never { * payload is available from the kernel side). */ export async function seaCancel( - state: SeaOperationLifecycleState, - statement: SeaStatementHandle, + 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}`); state.isCancelled = true; @@ -144,7 +144,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 @@ -156,8 +156,8 @@ export async function seaCancel( * sees the closed state as soon as the await yields. */ export async function seaClose( - state: SeaOperationLifecycleState, - statement: SeaStatementHandle, + state: KernelOperationLifecycleState, + statement: KernelStatementHandle, context: IClientContext, operationId: string, ): Promise { @@ -165,7 +165,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; @@ -199,7 +199,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. @@ -217,7 +217,7 @@ function synthesizeFinishedStatus(): OperationStatus { * fetch calls). */ export async function seaFinished( - state: SeaOperationLifecycleState, + state: KernelOperationLifecycleState, options?: { progress?: boolean; callback?: (status: OperationStatus) => unknown; @@ -236,15 +236,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 94% rename from lib/sea/SeaPositionalParams.ts rename to lib/kernel/KernelPositionalParams.ts index 0089d018..192dc0b9 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; @@ -95,7 +95,7 @@ function toTypedValueInput(value: DBSQLParameter | DBSQLParameterValue): SeaNati */ export function buildSeaPositionalParams( ordinalParameters?: Array, -): Array | undefined { +): Array | undefined { if (ordinalParameters === undefined || ordinalParameters.length === 0) { return undefined; } @@ -113,7 +113,7 @@ export function buildSeaPositionalParams( */ export function buildSeaNamedParams( 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 92% rename from lib/sea/SeaResultsProvider.ts rename to lib/kernel/KernelResultsProvider.ts index ce34641c..7262be9b 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>; } @@ -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 92% rename from lib/sea/SeaServerInfo.ts rename to lib/kernel/KernelServerInfo.ts index 955e006a..2d6ac4b1 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,7 +36,7 @@ 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. */ @@ -55,7 +55,7 @@ export const SEA_SERVER_NAME = 'Spark SQL'; export const SEA_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. diff --git a/lib/sea/SeaSessionBackend.ts b/lib/kernel/KernelSessionBackend.ts similarity index 84% rename from lib/sea/SeaSessionBackend.ts rename to lib/kernel/KernelSessionBackend.ts index bbd84cd4..ec8d6df9 100644 --- a/lib/sea/SeaSessionBackend.ts +++ b/lib/kernel/KernelSessionBackend.ts @@ -33,31 +33,31 @@ import InfoValue from '../dto/InfoValue'; import HiveDriverError from '../errors/HiveDriverError'; import ParameterError from '../errors/ParameterError'; import { LogLevel } from '../contracts/IDBSQLLogger'; -import { SeaConnection, SeaNativeExecuteOptions, SeaStatement } from './SeaNativeLoader'; -import { decodeNapiKernelError } from './SeaErrorMapping'; +import { KernelConnection, KernelNativeExecuteOptions, KernelStatement } from './KernelNativeLoader'; +import { decodeNapiKernelError } from './KernelErrorMapping'; import { numberToInt64 } from '../thrift-backend/ThriftSessionBackend'; -import SeaOperationBackend from './SeaOperationBackend'; -import { buildSeaPositionalParams, buildSeaNamedParams } from './SeaPositionalParams'; -import { seaServerInfoValue } from './SeaServerInfo'; +import KernelOperationBackend from './KernelOperationBackend'; +import { buildSeaPositionalParams, buildSeaNamedParams } from './KernelPositionalParams'; +import { seaServerInfoValue } 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`. * * **M0 scope:** `executeStatement` + `close`. Metadata methods * (`getCatalogs`, `getSchemas`, etc.) defer to M1 — they throw a clear - * `HiveDriverError` so consumers using SEA against metadata APIs get an + * `HiveDriverError` so consumers using kernel against metadata APIs get an * actionable message instead of silently falling back. The Thrift * backend continues to handle the metadata path by default (callers - * opt into SEA via `ConnectionOptions.useSEA`). + * opt into kernel via `ConnectionOptions.useKernel`). * * **Session config flow:** catalog / schema / sessionConf are applied * once at session creation (kernel `Session::builder().defaults()` + @@ -67,8 +67,8 @@ export interface SeaSessionBackendOptions { * needed — that pattern was removed when the napi binding moved these * onto `openSession` to match pyo3. */ -export default class SeaSessionBackend implements ISessionBackend { - private readonly connection: SeaConnection; +export default class KernelSessionBackend implements ISessionBackend { + private readonly connection: KernelConnection; private readonly context: IClientContext; @@ -76,7 +76,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(); @@ -87,12 +87,12 @@ export default class SeaSessionBackend implements ISessionBackend { } /** - * `getInfo` (JDBC `DatabaseMetaData` / ODBC `SQLGetInfo`) has no SEA/kernel + * `getInfo` (JDBC `DatabaseMetaData` / ODBC `SQLGetInfo`) has no kernel/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 @@ -106,10 +106,10 @@ export default class SeaSessionBackend implements ISessionBackend { const value = seaServerInfoValue(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); @@ -125,12 +125,12 @@ export default class SeaSessionBackend implements ISessionBackend { * - `queryTimeout` → enforced client-side by the operation backend's poll * deadline (the kernel ignores `queryTimeoutSecs` on the async submit * path), NOT forwarded to the napi options; - * - `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 @@ -141,17 +141,17 @@ 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', ); } @@ -176,7 +176,7 @@ export default class SeaSessionBackend implements ISessionBackend { // loop: the kernel ignores `queryTimeoutSecs` on the async submit path // (`submitStatement` always sends `wait_timeout=0s`), so we do NOT forward // it to the napi options — passing it there would be a silent no-op. - return new SeaOperationBackend({ + return new KernelOperationBackend({ asyncStatement: asyncStatement!, context: this.context, // `queryTimeout` is typed `number | bigint | Int64`; `numberToInt64(...).toNumber()` @@ -190,7 +190,7 @@ 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 @@ -201,7 +201,7 @@ export default class SeaSessionBackend implements ISessionBackend { throw new ParameterError('Driver does not support both ordinal and named parameters.'); } - const execOptions: SeaNativeExecuteOptions = {}; + const execOptions: KernelNativeExecuteOptions = {}; if (positionalParams !== undefined) { execOptions.positionalParams = positionalParams; } @@ -235,8 +235,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, @@ -300,13 +300,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/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(() => @@ -329,8 +329,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) { @@ -341,13 +341,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; } @@ -366,7 +366,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/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 92% rename from native/sea/README.md rename to native/kernel/README.md index 2b261f08..c95120ed 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; @@ -64,8 +64,8 @@ file at the repo root — a single 40-char commit SHA. The `.github/workflows/kernel-e2e.yml` CI job is the consumer: it reads `KERNEL_REV`, checks the kernel out at that SHA (via a GitHub App token with read access to `databricks/databricks-sql-kernel`), runs -`npm run build:native` against it, and runs the SEA e2e suite -(`tests/e2e/sea/**`) against the dogfood warehouse. **Bumping `KERNEL_REV` +`npm run build:native` against it, and runs the kernel e2e suite +(`tests/e2e/kernel/**`) against the dogfood warehouse. **Bumping `KERNEL_REV` is the only way to pick up a new kernel version** — so a driver change and the kernel revision it depends on always land together in one reviewable diff. @@ -77,7 +77,7 @@ that SHA (`git -C checkout "$(cat KERNEL_REV)"`) to match CI. At release time the kernel's CI publishes `@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. @@ -97,7 +97,7 @@ M0 targets a **single** triple: **`linux-x64-gnu`** (package `@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..11389894 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 SEA. 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 94% rename from tests/e2e/sea/auth-m2m-e2e.test.ts rename to tests/e2e/kernel/auth-m2m-e2e.test.ts index debd74a7..4fc22ba1 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: * 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 @@ -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('sea-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 91% rename from tests/e2e/sea/auth-pat-e2e.test.ts rename to tests/e2e/kernel/auth-pat-e2e.test.ts index d061d5b2..78ab2747 100644 --- a/tests/e2e/sea/auth-pat-e2e.test.ts +++ b/tests/e2e/kernel/auth-pat-e2e.test.ts @@ -20,7 +20,7 @@ import { InternalConnectionOptions } from '../../../lib/contracts/InternalConnec /** * sea-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. * @@ -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('sea-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 91% rename from tests/e2e/sea/auth-u2m-e2e.test.ts rename to tests/e2e/kernel/auth-u2m-e2e.test.ts index 923d5f0e..fe36fb16 100644 --- a/tests/e2e/sea/auth-u2m-e2e.test.ts +++ b/tests/e2e/kernel/auth-u2m-e2e.test.ts @@ -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('sea-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 86% rename from tests/e2e/sea/e2e-smoke.test.ts rename to tests/e2e/kernel/e2e-smoke.test.ts index e96efe34..32cdf530 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 { tryGetSeaNative, 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(); 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 87% rename from tests/e2e/sea/execution-e2e.test.ts rename to tests/e2e/kernel/execution-e2e.test.ts index 28dd1035..45b5a5f8 100644 --- a/tests/e2e/sea/execution-e2e.test.ts +++ b/tests/e2e/kernel/execution-e2e.test.ts @@ -20,14 +20,14 @@ import { InternalConnectionOptions } from '../../../lib/contracts/InternalConnec /** * sea-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 + * sea-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,11 +79,11 @@ 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 + // M0 does not yet plumb fetchChunk through the kernel pipeline // (sea-results owns that). We exercise the lifecycle: cancel is a // no-op against a finished statement, close releases the kernel // handle. @@ -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 { 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(); diff --git a/tests/e2e/sea/operation-lifecycle-e2e.test.ts b/tests/e2e/kernel/operation-lifecycle-e2e.test.ts similarity index 89% rename from tests/e2e/sea/operation-lifecycle-e2e.test.ts rename to tests/e2e/kernel/operation-lifecycle-e2e.test.ts index 5d529aca..87c7b176 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 * 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 { getSeaNative } 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 @@ -117,7 +117,7 @@ describe('SEA operation lifecycle — end-to-end', function suite() { } }); - it('cancel() succeeds against a live SEA statement', async () => { + it('cancel() succeeds against a live kernel statement', async () => { const binding = getSeaNative() as unknown as NativeBinding; const connection = await binding.openSession({ @@ -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(), }); @@ -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,7 +202,7 @@ describe('SEA operation lifecycle — end-to-end', function suite() { } }); - it('close() succeeds against a SEA statement and is idempotent', async () => { + it('close() succeeds against a kernel statement and is idempotent', async () => { const binding = getSeaNative() as unknown as NativeBinding; const connection = await binding.openSession({ @@ -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(), }); @@ -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 90% rename from tests/e2e/sea/results-e2e.test.ts rename to tests/e2e/kernel/results-e2e.test.ts index 497889c5..3ddc6374 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,7 +120,7 @@ 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 () => { + it('Thrift and kernel produce byte-identical rows for the probe query (parity gate)', async () => { const seaRows = await fetchProbeRows(true, secrets as PecoSecrets); const thriftRows = await fetchProbeRows(false, secrets as PecoSecrets); expect(seaRows.map(canonical)).to.deep.equal(thriftRows.map(canonical)); diff --git a/tests/unit/DBSQLClient.test.ts b/tests/unit/DBSQLClient.test.ts index 81d41f2e..abf71e83 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 seaOptions = { ...connectOptions, token: '', useKernel: true } as any; try { await client.connect(seaOptions); - expect.fail('SeaBackend connect() should reject (empty PAT / absent native binding)'); + 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 bee5a5b9..1938845c 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. const metadata = await backend.getResultMetadata(); @@ -426,7 +426,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'); @@ -434,7 +434,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 93% rename from tests/unit/sea/auth-edge-cases.test.ts rename to tests/unit/kernel/auth-edge-cases.test.ts index 9f0928bb..808a0135 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 { buildSeaConnectionOptions } 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 = { @@ -340,7 +340,7 @@ describe('SeaAuth — edge cases (input validation + ambiguity)', () => { }); }); -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 92% rename from tests/unit/sea/auth-m2m.test.ts rename to tests/unit/kernel/auth-m2m.test.ts index 159afe1d..6c198a70 100644 --- a/tests/unit/sea/auth-m2m.test.ts +++ b/tests/unit/kernel/auth-m2m.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 { buildSeaConnectionOptions } 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('KernelAuth + KernelBackend — OAuth M2M auth flow', () => { describe('buildSeaConnectionOptions', () => { it('accepts databricks-oauth + oauthClientId + oauthClientSecret', () => { const opts: ConnectionOptions = { @@ -143,10 +143,10 @@ describe('SeaAuth + SeaBackend — OAuth M2M auth flow', () => { }); }); - 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 +157,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 +178,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 92% rename from tests/unit/sea/auth-pat.test.ts rename to tests/unit/kernel/auth-pat.test.ts index bd82eb87..ae44361c 100644 --- a/tests/unit/sea/auth-pat.test.ts +++ b/tests/unit/kernel/auth-pat.test.ts @@ -13,12 +13,12 @@ // limitations under the License. import { expect } from 'chai'; -import { buildSeaConnectionOptions } from '../../../lib/sea/SeaAuth'; +import { buildSeaConnectionOptions } 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('KernelAuth — PAT auth options builder', () => { describe('buildSeaConnectionOptions', () => { it('accepts a bare access-token PAT (undefined authType)', () => { const opts: ConnectionOptions = { @@ -121,9 +121,9 @@ describe('SeaAuth — PAT auth options builder', () => { }); }); - // 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 sea-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 diff --git a/tests/unit/sea/auth-u2m.test.ts b/tests/unit/kernel/auth-u2m.test.ts similarity index 92% rename from tests/unit/sea/auth-u2m.test.ts rename to tests/unit/kernel/auth-u2m.test.ts index 828ca961..ffc16d51 100644 --- a/tests/unit/sea/auth-u2m.test.ts +++ b/tests/unit/kernel/auth-u2m.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 { buildSeaConnectionOptions } 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('KernelAuth + KernelBackend — OAuth U2M auth flow', () => { describe('buildSeaConnectionOptions', () => { it('accepts databricks-oauth with no clientSecret as the U2M happy path (hardcoded port 8030)', () => { const opts: ConnectionOptions = { @@ -112,10 +112,10 @@ describe('SeaAuth + SeaBackend — OAuth U2M auth flow', () => { }); }); - 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 +124,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 93% rename from tests/unit/sea/connectionOptions.test.ts rename to tests/unit/kernel/connectionOptions.test.ts index 96e3f87a..a39f5b78 100644 --- a/tests/unit/sea/connectionOptions.test.ts +++ b/tests/unit/kernel/connectionOptions.test.ts @@ -13,17 +13,17 @@ // limitations under the License. import { expect } from 'chai'; -import { buildSeaConnectionOptions, buildSeaTlsOptions } from '../../../lib/sea/SeaAuth'; +import { buildSeaConnectionOptions, buildSeaTlsOptions } 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 }; expect(native.intervalsAsString).to.equal(true); @@ -35,7 +35,7 @@ describe('SeaAuth connection options — intervalsAsString default', () => { }); }); -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 }; expect(native.maxConnections).to.equal(10); @@ -63,7 +63,7 @@ describe('SeaAuth connection options — maxConnections', () => { }); }); -describe('SeaAuth TLS options (buildSeaTlsOptions)', () => { +describe('KernelAuth TLS options (buildSeaTlsOptions)', () => { it('is empty by default (secure-by-default — kernel default verify-on)', () => { expect(buildSeaTlsOptions(opts({}))).to.deep.equal({}); }); 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 90% rename from tests/unit/sea/execution.test.ts rename to tests/unit/kernel/execution.test.ts index dcd9561b..614c7ad7 100644 --- a/tests/unit/sea/execution.test.ts +++ b/tests/unit/kernel/execution.test.ts @@ -15,10 +15,10 @@ import { expect } from 'chai'; import sinon from 'sinon'; import Int64 from 'node-int64'; -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'; @@ -33,7 +33,7 @@ import { OperationState } from '../../../lib/contracts/OperationStatus'; // pulling in test-only fixtures from outside the sea/ namespace. // ----------------------------------------------------------------------------- -class FakeNativeStatement implements SeaStatement { +class FakeNativeStatement implements KernelStatement { public closed = false; public cancelled = false; @@ -123,7 +123,7 @@ class FakeAsyncStatement { } } -class FakeNativeConnection implements SeaConnection { +class FakeNativeConnection implements KernelConnection { public closed = false; public lastSql?: string; @@ -149,9 +149,9 @@ class FakeNativeConnection implements SeaConnection { // The async submit state(s) the next FakeAsyncStatement should report. public submitStatusValue: string | string[] = 'Succeeded'; - // The blocking executeStatement path is no longer used by the SEA backend + // The blocking executeStatement path is no longer used by the kernel backend // (queries go through submitStatement), but the binding still exposes it. - public async executeStatement(sql: string, options?: unknown): Promise { + public async executeStatement(sql: string, options?: unknown): Promise { if (this.throwOnExecute) { throw this.throwOnExecute; } @@ -172,7 +172,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); } @@ -236,7 +236,7 @@ class FakeNativeConnection implements SeaConnection { } } -function makeBinding(connection: SeaConnection): SeaNativeBinding & { +function makeBinding(connection: KernelConnection): KernelNativeBinding & { openSessionStub: sinon.SinonStub; } { const openSessionStub = sinon.stub().resolves(connection); @@ -248,7 +248,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 }); } @@ -263,13 +263,13 @@ function makeContext(): IClientContext { getConfig: () => config, getLogger: () => logger, 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'); }, }; } @@ -278,11 +278,11 @@ function makeContext(): 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', @@ -303,7 +303,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 { @@ -323,7 +323,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 { @@ -337,7 +337,7 @@ describe('SeaBackend', () => { } expect(thrown).to.be.instanceOf(HiveDriverError); // After sea-integration merge, missing-token validation goes through - // SeaAuth.buildSeaConnectionOptions which throws AuthenticationError + // KernelAuth.buildSeaConnectionOptions which throws AuthenticationError // (extends HiveDriverError) with the "non-empty PAT" message. expect((thrown as Error).message).to.match(/non-empty PAT/); }); @@ -345,7 +345,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 { @@ -360,7 +360,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', @@ -372,9 +372,9 @@ describe('SeaBackend', () => { expect(binding.openSessionStub.calledOnce).to.equal(true); const args = binding.openSessionStub.firstCall.args[0]; - // sea-auth-u2m introduced the discriminated SeaNativeConnectionOptions + // sea-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', @@ -385,10 +385,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', @@ -397,14 +397,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', @@ -439,7 +439,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(); @@ -453,9 +453,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 () => { @@ -465,11 +465,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); }); @@ -575,7 +575,7 @@ describe('SeaSessionBackend', () => { expect(conf?.query_tags).to.contain('team:x'); }); - // 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 [ @@ -598,14 +598,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'] }); @@ -707,9 +707,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', () => { @@ -752,26 +752,29 @@ describe('SeaOperationBackend', () => { }); // Note: after sea-integration merge, fetchChunk is no longer a stub — - // the sea-results SeaResultsProvider + ArrowResultConverter pipeline + // the sea-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, queryTimeoutSecs?: number) => // eslint-disable-next-line @typescript-eslint/no-explicit-any - new SeaOperationBackend({ asyncStatement: asyncStatement as any, context: makeContext(), queryTimeoutSecs }); + new KernelOperationBackend({ asyncStatement: asyncStatement as any, context: makeContext(), queryTimeoutSecs }); 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(), 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 13d88632..deff4161 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,14 +25,14 @@ 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 () => ({}), Connection: function Connection() {}, Statement: function Statement() {}, ...overrides, - } as unknown as SeaNativeBinding; + } as unknown as KernelNativeBinding; } function errWithCode(code: string, message: string): NodeJS.ErrnoException { @@ -53,11 +53,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); }); @@ -65,7 +65,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); @@ -78,8 +78,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()); @@ -88,7 +88,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()); @@ -98,14 +98,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); @@ -115,7 +115,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/); @@ -126,7 +126,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(); @@ -138,7 +138,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/native-packaging.test.ts b/tests/unit/kernel/native-packaging.test.ts similarity index 86% rename from tests/unit/sea/native-packaging.test.ts rename to tests/unit/kernel/native-packaging.test.ts index b2732673..f9824780 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/sql-kernel-` (see native/sea/README.md and the -// SeaNativeLoader load-failure hint). -describe('SEA native binding — packaging (native/sea/index.js)', () => { +// `@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/sql-kernel- name', () => { const triple = /^@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 88% rename from tests/unit/sea/operation-lifecycle.test.ts rename to tests/unit/kernel/operation-lifecycle.test.ts index 06260542..ae8f21b7 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, 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,7 +79,7 @@ function makeStatement(overrides: Partial = {}): { }; } -describe('SeaOperationLifecycle (helpers)', () => { +describe('KernelOperationLifecycle (helpers)', () => { describe('seaCancel', () => { it('calls statement.cancel() and resolves with a success Status', async () => { const ctx = makeContext(); @@ -125,7 +125,7 @@ describe('SeaOperationLifecycle (helpers)', () => { const cancelPromise = new Promise((resolve) => { release = resolve; }); - const handle: SeaStatementHandle = { + const handle: KernelStatementHandle = { cancel: () => cancelPromise, close: async () => undefined, }; @@ -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({ @@ -207,7 +207,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({ @@ -310,11 +310,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(); @@ -325,7 +325,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(); @@ -336,7 +336,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(); @@ -350,7 +350,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(); @@ -367,7 +367,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(); @@ -379,7 +379,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(); @@ -390,7 +390,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); @@ -399,7 +399,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); @@ -409,7 +409,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'); @@ -418,7 +418,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}$/); @@ -427,7 +427,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 96% rename from tests/unit/sea/positionalParams.test.ts rename to tests/unit/kernel/positionalParams.test.ts index d9902303..eda84b6e 100644 --- a/tests/unit/sea/positionalParams.test.ts +++ b/tests/unit/kernel/positionalParams.test.ts @@ -13,11 +13,11 @@ // limitations under the License. import { expect } from 'chai'; -import { buildSeaPositionalParams, buildSeaNamedParams } from '../../../lib/sea/SeaPositionalParams'; +import { buildSeaPositionalParams, buildSeaNamedParams } from '../../../lib/kernel/KernelPositionalParams'; import { DBSQLParameter, DBSQLParameterType } from '../../../lib/DBSQLParameter'; import ParameterError from '../../../lib/errors/ParameterError'; -describe('SeaPositionalParams.buildSeaPositionalParams', () => { +describe('KernelPositionalParams.buildSeaPositionalParams', () => { it('returns undefined for no params (keeps the no-options fast path)', () => { expect(buildSeaPositionalParams(undefined)).to.equal(undefined); expect(buildSeaPositionalParams([])).to.equal(undefined); @@ -104,7 +104,7 @@ describe('SeaPositionalParams.buildSeaPositionalParams', () => { }); }); -describe('SeaPositionalParams.buildSeaNamedParams', () => { +describe('KernelPositionalParams.buildSeaNamedParams', () => { it('returns undefined for no named params', () => { expect(buildSeaNamedParams(undefined)).to.equal(undefined); expect(buildSeaNamedParams({})).to.equal(undefined); diff --git a/tests/unit/sea/serverInfo.test.ts b/tests/unit/kernel/serverInfo.test.ts similarity index 91% rename from tests/unit/sea/serverInfo.test.ts rename to tests/unit/kernel/serverInfo.test.ts index 4a027864..9ffd6771 100644 --- a/tests/unit/sea/serverInfo.test.ts +++ b/tests/unit/kernel/serverInfo.test.ts @@ -13,10 +13,15 @@ // limitations under the License. import { expect } from 'chai'; -import { seaServerInfoValue, SEA_DBMS_NAME, SEA_SERVER_NAME, SEA_DBMS_VERSION } from '../../../lib/sea/SeaServerInfo'; +import { + seaServerInfoValue, + SEA_DBMS_NAME, + SEA_SERVER_NAME, + SEA_DBMS_VERSION, +} from '../../../lib/kernel/KernelServerInfo'; import { TGetInfoType } from '../../../thrift/TCLIService_types'; -describe('SeaServerInfo.seaServerInfoValue', () => { +describe('KernelServerInfo.seaServerInfoValue', () => { 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'); diff --git a/tests/unit/sea/version.test.ts b/tests/unit/kernel/version.test.ts similarity index 89% rename from tests/unit/sea/version.test.ts rename to tests/unit/kernel/version.test.ts index 24a05d7a..f747b9c2 100644 --- a/tests/unit/sea/version.test.ts +++ b/tests/unit/kernel/version.test.ts @@ -13,7 +13,7 @@ // limitations under the License. import { expect } from 'chai'; -import { tryGetSeaNative } from '../../../lib/sea/SeaNativeLoader'; +import { tryGetSeaNative } 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/sql-kernel-*` @@ -29,14 +29,14 @@ function bindingIsExpected(): boolean { return process.env.SEA_NATIVE_EXPECTED === '1'; } -describe('SEA native binding — smoke test', function smoke() { +describe('kernel native binding — smoke test', function smoke() { const binding = tryGetSeaNative(); 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/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; }