Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 16 additions & 7 deletions lib/sea/SeaArrowIpc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,12 @@ function arrowTypeToTTypeId(field: Field<DataType>): TTypeId {
return TTypeId.TIMESTAMP_TYPE;
case 'DECIMAL':
return TTypeId.DECIMAL_TYPE;
// INTERVAL — surface as STRING_TYPE to match the Thrift backend and the
// Python kernel connector, both of which report interval columns with a
// string type code. The cell value is already rendered to the canonical
// interval string (e.g. "2-6" / "3 12:30:15.000000000") by
// ArrowResultConverter, which keys off the Arrow value type (not this
// synthesized TTypeId), so value formatting is unaffected.
case 'INTERVAL':
case 'INTERVAL DAY':
case 'INTERVAL DAY TO HOUR':
Expand All @@ -173,11 +179,10 @@ function arrowTypeToTTypeId(field: Field<DataType>): TTypeId {
case 'INTERVAL MINUTE':
case 'INTERVAL MINUTE TO SECOND':
case 'INTERVAL SECOND':
return TTypeId.INTERVAL_DAY_TIME_TYPE;
case 'INTERVAL YEAR':
case 'INTERVAL YEAR TO MONTH':
case 'INTERVAL MONTH':
return TTypeId.INTERVAL_YEAR_MONTH_TYPE;
return TTypeId.STRING_TYPE;
case 'ARRAY':
return TTypeId.ARRAY_TYPE;
case 'MAP':
Expand All @@ -198,10 +203,12 @@ function arrowTypeToTTypeId(field: Field<DataType>): 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
// so the converter formats them back into the thrift string form.
// `SeaArrowIpcDurationFix.ts`). Surface them as STRING_TYPE (matching the
// Thrift backend and Python kernel) — the converter still formats the
// value into the thrift INTERVAL DAY-TIME string via the duration_unit
// metadata, independent of this type code.
if (arrowType.bitWidth === 64 && field.metadata.has(DURATION_UNIT_METADATA_KEY)) {
return TTypeId.INTERVAL_DAY_TIME_TYPE;
return TTypeId.STRING_TYPE;
}
switch (arrowType.bitWidth) {
case 8:
Expand Down Expand Up @@ -233,8 +240,10 @@ function arrowTypeToTTypeId(field: Field<DataType>): TTypeId {
// pairs which the converter formats to thrift's `"Y-M"` / day-time
// strings.
if (DataType.isInterval(arrowType)) {
// unit 0 = YEAR_MONTH, unit 1 = DAY_TIME, unit 2 = MONTH_DAY_NANO
return arrowType.unit === 0 ? TTypeId.INTERVAL_YEAR_MONTH_TYPE : TTypeId.INTERVAL_DAY_TIME_TYPE;
// Surface native Arrow interval types as STRING_TYPE too (Thrift / Python
// kernel parity). The converter formats the value to the thrift "Y-M" /
// day-time string from the Arrow value, independent of this type code.
return TTypeId.STRING_TYPE;
}
if (DataType.isList(arrowType)) return TTypeId.ARRAY_TYPE;
if (DataType.isMap(arrowType)) return TTypeId.MAP_TYPE;
Expand Down
15 changes: 14 additions & 1 deletion lib/sea/SeaErrorMapping.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,20 @@ export function mapKernelErrorToJsError(kErr: KernelErrorShape): ErrorWithSqlSta
error = new ParameterError(message);
break;

case 'SqlError': {
// A server-reported SQL execution failure (kernel `SqlError`, e.g. a
// bad query, missing table, divide-by-zero, invalid cast). The Thrift
// backend surfaces the same situation as `OperationStateError(Error)`
// when the operation reaches ERROR_STATE (see DBSQLOperation), so map
// SqlError to the same class for backend parity. OperationStateError
// extends HiveDriverError, so existing `instanceof HiveDriverError`
// catches are unaffected.
const stateError = new OperationStateError(OperationStateErrorCode.Error);
stateError.message = message;
error = stateError;
break;
}

// All remaining kernel ErrorCode variants map to the base driver error class.
// M0 intentionally does not introduce new error classes; M1 may add nuance.
case 'NotFound':
Expand All @@ -156,7 +170,6 @@ export function mapKernelErrorToJsError(kErr: KernelErrorShape): ErrorWithSqlSta
case 'Internal':
case 'InvalidStatementHandle':
case 'NetworkError':
case 'SqlError':
error = new HiveDriverError(message);
break;

Expand Down
9 changes: 6 additions & 3 deletions tests/unit/sea/SeaIntervalParity.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -403,12 +403,15 @@ describe('SeaOperationBackend — INTERVAL parity with thrift', () => {
const backend = new SeaOperationBackend({ statement: stub, context: new ClientContextStub() });

// Round-trip the metadata to confirm we synthesise the right TTypeId.
// Interval columns are surfaced as STRING_TYPE — matching the Thrift
// backend and the Python kernel connector, both of which report interval
// columns with a string type code. The value is still rendered to the
// canonical interval string (asserted below), which is what makes this
// "interval parity with thrift".
const metadata = await backend.getResultMetadata();
expect(metadata.schema?.columns?.[0]?.typeDesc.types?.[0]?.primitiveEntry?.type).to.equal(
// INTERVAL_DAY_TIME_TYPE = 30 in TCLIService_types
// We assert by importing the enum below to avoid magic numbers.
// eslint-disable-next-line global-require, @typescript-eslint/no-var-requires
require('../../../thrift/TCLIService_types').TTypeId.INTERVAL_DAY_TIME_TYPE,
require('../../../thrift/TCLIService_types').TTypeId.STRING_TYPE,
);

const rows = await backend.fetchChunk({ limit: 100 });
Expand Down
Loading