Skip to content

Commit 327f1a2

Browse files
committed
sea: complete neutral-type conformance so the SEA driver builds + runs
Finishes the IOperationBackend neutral-type WIP that blocked the SEA driver from compiling against the merged abstraction: - SeaOperationBackend.status() returns the neutral OperationStatus (OperationState.{Cancelled,Closed,Succeeded}) instead of a Thrift TGetOperationStatusResp; getResultMetadata() returns the neutral ResultMetadata (schema + ResultFormat.ArrowBased + arrowSchema) instead of TGetResultSetMetadataResp. The IOperationBackend contract is neutral; the Thrift backend adapts at its own boundary. - ArrowResultConverter only consumes `schema`, so its ctor param is narrowed from TGetResultSetMetadataResp to `{ schema?: TTableSchema }` — now satisfied by BOTH the Thrift resp and the neutral ResultMetadata (DRY: no per-backend adapter at the call site). - SeaBackend casts the SeaAuth options union to the binding's openSession param at the single boundary (authMode string-literal vs napi const enum — same runtime values; cast localized per SeaAuth's const-enum note). Validated locally end-to-end: the built DBSQLClient on the SEA backend runs `SELECT 1, current_catalog()` → correct rows, and the databricks- driver-test Node comparator (PR #281) runs thrift-vs-SEA with conn1 (Thrift) and conn2 (useSEA) both opening + querying OK. Co-authored-by: Isaac
1 parent cea1925 commit 327f1a2

3 files changed

Lines changed: 39 additions & 39 deletions

File tree

lib/result/ArrowResultConverter.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import {
1313
RecordBatchReader,
1414
util as arrowUtils,
1515
} from 'apache-arrow';
16-
import { TGetResultSetMetadataResp, TColumnDesc } from '../../thrift/TCLIService_types';
16+
import { TTableSchema, TColumnDesc } from '../../thrift/TCLIService_types';
1717
import IClientContext from '../contracts/IClientContext';
1818
import IResultsProvider, { ResultsProviderFetchNextOptions } from './IResultsProvider';
1919
import { ArrowBatch, getSchemaColumns, convertThriftValue } from './utils';
@@ -179,7 +179,12 @@ export default class ArrowResultConverter implements IResultsProvider<Array<any>
179179
// actually return a non-empty result
180180
private prefetchedRecordBatch?: RecordBatch<TypeMap>;
181181

182-
constructor(context: IClientContext, source: IResultsProvider<ArrowBatch>, { schema }: TGetResultSetMetadataResp) {
182+
// Only the column `schema` is consumed here. Typed as the minimal shape
183+
// (not the full Thrift `TGetResultSetMetadataResp`) so both the Thrift
184+
// operation backend and the SEA backend's neutral `ResultMetadata` —
185+
// which both carry `schema?: TTableSchema` — can construct the converter
186+
// without an adapter at the call site.
187+
constructor(context: IClientContext, source: IResultsProvider<ArrowBatch>, { schema }: { schema?: TTableSchema }) {
183188
this.context = context;
184189
this.source = source;
185190
this.schema = getSchemaColumns(schema);

lib/sea/SeaBackend.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,15 @@ export default class SeaBackend implements IBackend {
111111

112112
let nativeConnection: SeaNativeConnection;
113113
try {
114-
nativeConnection = (await this.binding.openSession(sessionOptions)) as SeaNativeConnection;
114+
// `SeaNativeConnectionOptions.authMode` is a string-literal union
115+
// ('Pat' | 'OAuthM2m' | 'OAuthU2m') — deliberately not the binding's
116+
// `const enum AuthMode` (see SeaAuth's note on why a const-enum import
117+
// is avoided). The literal values are byte-identical to the enum's, so
118+
// the only divergence is TS's const-enum strictness; cast to the
119+
// binding's parameter type at this single boundary.
120+
nativeConnection = (await this.binding.openSession(
121+
sessionOptions as unknown as Parameters<SeaNativeBinding['openSession']>[0],
122+
)) as SeaNativeConnection;
115123
} catch (err) {
116124
throw decodeNapiKernelError(err);
117125
}

lib/sea/SeaOperationBackend.ts

Lines changed: 23 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -37,15 +37,10 @@
3737
*/
3838

3939
import { v4 as uuidv4 } from 'uuid';
40-
import {
41-
TGetOperationStatusResp,
42-
TGetResultSetMetadataResp,
43-
TOperationState,
44-
TSparkRowSetType,
45-
TStatusCode,
46-
TTableSchema,
47-
} from '../../thrift/TCLIService_types';
40+
import { TGetOperationStatusResp, TTableSchema } from '../../thrift/TCLIService_types';
4841
import IOperationBackend from '../contracts/IOperationBackend';
42+
import { OperationStatus, OperationState } from '../contracts/OperationStatus';
43+
import { ResultMetadata, ResultFormat } from '../contracts/ResultMetadata';
4944
import IClientContext from '../contracts/IClientContext';
5045
import Status from '../dto/Status';
5146
import ArrowResultConverter from '../result/ArrowResultConverter';
@@ -101,9 +96,9 @@ export default class SeaOperationBackend implements IOperationBackend {
10196

10297
private resultsProvider?: SeaResultsProvider;
10398

104-
private metadata?: TGetResultSetMetadataResp;
99+
private metadata?: ResultMetadata;
105100

106-
private metadataPromise?: Promise<TGetResultSetMetadataResp>;
101+
private metadataPromise?: Promise<ResultMetadata>;
107102

108103
constructor({ statement, context, id }: SeaOperationBackendOptions) {
109104
this.statement = statement;
@@ -148,7 +143,7 @@ export default class SeaOperationBackend implements IOperationBackend {
148143
return slicer.hasMore();
149144
}
150145

151-
public async getResultMetadata(): Promise<TGetResultSetMetadataResp> {
146+
public async getResultMetadata(): Promise<ResultMetadata> {
152147
failIfNotActive(this.lifecycle);
153148
if (this.metadata) {
154149
return this.metadata;
@@ -162,15 +157,17 @@ export default class SeaOperationBackend implements IOperationBackend {
162157
}
163158
const arrowSchemaIpc = await this.statement.schema();
164159
const arrowSchema = decodeIpcSchema(arrowSchemaIpc.ipcBytes);
160+
// `ResultMetadata.schema` keeps the Thrift `TTableSchema` shape for
161+
// back-compat with the public `IOperation.getSchema()` surface.
165162
const thriftSchema: TTableSchema = arrowSchemaToThriftSchema(arrowSchema);
166-
const meta: TGetResultSetMetadataResp = {
167-
status: { statusCode: TStatusCode.SUCCESS_STATUS },
163+
const meta: ResultMetadata = {
168164
schema: thriftSchema,
169165
// SEA inline + CloudFetch both surface to JS as Arrow batches;
170-
// both flow through the same converter that handles the
171-
// ARROW_BASED_SET path on the thrift side.
172-
resultFormat: TSparkRowSetType.ARROW_BASED_SET,
166+
// both flow through the same Arrow result converter.
167+
resultFormat: ResultFormat.ArrowBased,
173168
lz4Compressed: false,
169+
// Carry the raw Arrow IPC schema bytes for ARROW_BASED consumers.
170+
arrowSchema: arrowSchemaIpc.ipcBytes,
174171
isStagingOperation: false,
175172
};
176173
this.metadata = meta;
@@ -187,30 +184,20 @@ export default class SeaOperationBackend implements IOperationBackend {
187184
// Status / lifecycle (owned by the sea-operation lifecycle helpers).
188185
// ---------------------------------------------------------------------------
189186

190-
public async status(_progress: boolean): Promise<TGetOperationStatusResp> {
191-
// Synthesised — kernel only surfaces terminal-or-running statements
192-
// through its public API; we report CANCELED/CLOSED if the lifecycle
193-
// flag is set, else FINISHED. Matches the Thrift status shape so
194-
// facade-level callers see consistent telemetry across backends.
187+
public async status(_progress: boolean): Promise<OperationStatus> {
188+
// Synthesised — the kernel resolves `Statement::execute().await` before
189+
// it hands back a Statement handle, so by the time a SeaOperationBackend
190+
// exists the statement is terminal. Report Cancelled/Closed if the
191+
// lifecycle flag is set, else Succeeded. Returns the backend-neutral
192+
// OperationStatus the IOperationBackend contract expects, so the
193+
// DBSQLOperation facade switches on `state` identically across backends.
195194
if (this.lifecycle.isCancelled) {
196-
return {
197-
status: { statusCode: TStatusCode.SUCCESS_STATUS },
198-
operationState: TOperationState.CANCELED_STATE,
199-
hasResultSet: true,
200-
};
195+
return { state: OperationState.Cancelled, hasResultSet: true };
201196
}
202197
if (this.lifecycle.isClosed) {
203-
return {
204-
status: { statusCode: TStatusCode.SUCCESS_STATUS },
205-
operationState: TOperationState.CLOSED_STATE,
206-
hasResultSet: true,
207-
};
198+
return { state: OperationState.Closed, hasResultSet: true };
208199
}
209-
return {
210-
status: { statusCode: TStatusCode.SUCCESS_STATUS },
211-
operationState: TOperationState.FINISHED_STATE,
212-
hasResultSet: true,
213-
};
200+
return { state: OperationState.Succeeded, hasResultSet: true };
214201
}
215202

216203
public async waitUntilReady(options?: {

0 commit comments

Comments
 (0)